1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-12 17:03:10 +00:00

Compare commits

...

4 Commits

Author SHA1 Message Date
JKorf 7ecf37064b Updated version 2023-06-29 20:10:35 +02:00
JKorf 18954f4f53 Added optional log level parameter for trace logger 2023-06-29 20:07:26 +02:00
JKorf 00bc245102 Updated examples 2023-06-26 20:45:28 +02:00
JKorf 273cab9fdb Docs 2023-06-25 21:43:30 +02:00
23 changed files with 244 additions and 604 deletions
+4 -4
View File
@@ -6,16 +6,16 @@
<PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors>
<Description>A base package for implementing cryptocurrency API's</Description>
<PackageVersion>6.0.0</PackageVersion>
<AssemblyVersion>6.0.0</AssemblyVersion>
<FileVersion>6.0.0</FileVersion>
<PackageVersion>6.0.1</PackageVersion>
<AssemblyVersion>6.0.1</AssemblyVersion>
<FileVersion>6.0.1</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
<NeutralLanguage>en</NeutralLanguage>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>6.0.0 - Updated ApiCredentials to support RSA signing as well as the default Hmac signature, Removed custom logging implementation in favor of using `Microsoft.Extensions.Logging` ILogger directly, Refactored client options for easier use, Added easier way of switching environments, Added ResponseLength and ToString() override on WebCallResult object, Fixed memory leak in AsyncResetEvent</PackageReleaseNotes>
<PackageReleaseNotes>6.0.1 - Added LogLevel optional parameter to TraceLoggerProvider</PackageReleaseNotes>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
-329
View File
@@ -1,329 +0,0 @@
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Net.Http;
//using CryptoExchange.Net.Authentication;
//using CryptoExchange.Net.Interfaces;
//using CryptoExchange.Net.Logging;
//using Microsoft.Extensions.Logging;
//namespace CryptoExchange.Net.Objects
//{
// /// <summary>
// /// Client options
// /// </summary>
// public abstract class ClientOptions
// {
// internal event Action? OnLoggingChanged;
// private LogLevel _logLevel = LogLevel.Information;
// /// <summary>
// /// The minimum log level to output
// /// </summary>
// public LogLevel LogLevel
// {
// get => _logLevel;
// set
// {
// _logLevel = value;
// OnLoggingChanged?.Invoke();
// }
// }
// private List<ILogger> _logWriters = new List<ILogger> { new DebugLogger() };
// /// <summary>
// /// The log writers
// /// </summary>
// public List<ILogger> LogWriters
// {
// get => _logWriters;
// set
// {
// _logWriters = value;
// OnLoggingChanged?.Invoke();
// }
// }
// /// <summary>
// /// Proxy to use when connecting
// /// </summary>
// public ApiProxy? Proxy { get; set; }
// /// <summary>
// /// The api credentials used for signing requests to this API.
// /// </summary>
// public ApiCredentials? ApiCredentials { get; set; }
// /// <summary>
// /// ctor
// /// </summary>
// public ClientOptions()
// {
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="clientOptions">Copy values for the provided options</param>
// public ClientOptions(ClientOptions? clientOptions)
// {
// if (clientOptions == null)
// return;
// LogLevel = clientOptions.LogLevel;
// LogWriters = clientOptions.LogWriters.ToList();
// Proxy = clientOptions.Proxy;
// ApiCredentials = clientOptions.ApiCredentials?.Copy();
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseOptions">Copy values for the provided options</param>
// /// <param name="newValues">Copy values for the provided options</param>
// internal ClientOptions(ClientOptions baseOptions, ClientOptions? newValues)
// {
// Proxy = newValues?.Proxy ?? baseOptions.Proxy;
// LogLevel = baseOptions.LogLevel;
// LogWriters = baseOptions.LogWriters.ToList();
// ApiCredentials = newValues?.ApiCredentials?.Copy() ?? baseOptions.ApiCredentials?.Copy();
// }
// /// <inheritdoc />
// public override string ToString()
// {
// return $"LogLevel: {LogLevel}, Writers: {LogWriters.Count}, Proxy: {(Proxy == null ? "-" : Proxy.Host)}";
// }
// }
// /// <summary>
// /// API client options
// /// </summary>
// public class ApiClientOptions
// {
// /// <summary>
// /// If true, the CallResult and DataEvent objects will also include the originally received json data in the OriginalData property
// /// </summary>
// public bool OutputOriginalData { get; set; } = false;
// /// <summary>
// /// The base address of the API
// /// </summary>
// public string BaseAddress { get; set; }
// /// <summary>
// /// The api credentials used for signing requests to this API. Overrides API credentials provided in the client options
// /// </summary>
// public ApiCredentials? ApiCredentials { get; set; }
// /// <summary>
// /// ctor
// /// </summary>
//#pragma warning disable 8618 // Will always get filled by the implementation
// public ApiClientOptions()
// {
// }
//#pragma warning restore 8618
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseAddress">Base address for the API</param>
// public ApiClientOptions(string baseAddress)
// {
// BaseAddress = baseAddress;
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseOptions">Copy values for the provided options</param>
// /// <param name="newValues">Copy values for the provided options</param>
// public ApiClientOptions(ApiClientOptions baseOptions, ApiClientOptions? newValues)
// {
// BaseAddress = newValues?.BaseAddress ?? baseOptions.BaseAddress;
// ApiCredentials = newValues?.ApiCredentials?.Copy() ?? baseOptions.ApiCredentials?.Copy();
// OutputOriginalData = newValues?.OutputOriginalData ?? baseOptions.OutputOriginalData;
// }
// /// <inheritdoc />
// public override string ToString()
// {
// return $"OutputOriginalData: {OutputOriginalData}, Credentials: {(ApiCredentials == null ? "-" : "Set")}, BaseAddress: {BaseAddress}";
// }
// }
// /// <summary>
// /// Rest API client options
// /// </summary>
// public class RestApiClientOptions: ApiClientOptions
// {
// /// <summary>
// /// The time the server has to respond to a request before timing out
// /// </summary>
// public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
// /// <summary>
// /// Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options provided in these options will be ignored in requests and should be set on the provided HttpClient instance
// /// </summary>
// public HttpClient? HttpClient { get; set; }
// /// <summary>
// /// List of rate limiters to use
// /// </summary>
// public List<IRateLimiter> RateLimiters { get; set; } = new List<IRateLimiter>();
// /// <summary>
// /// What to do when a call would exceed the rate limit
// /// </summary>
// public RateLimitingBehaviour RateLimitingBehaviour { get; set; } = RateLimitingBehaviour.Wait;
// /// <summary>
// /// Whether or not to automatically sync the local time with the server time
// /// </summary>
// public bool AutoTimestamp { get; set; }
// /// <summary>
// /// How often the timestamp adjustment between client and server is recalculated. If you need a very small TimeSpan here you're probably better of syncing your server time more often
// /// </summary>
// public TimeSpan TimestampRecalculationInterval { get; set; } = TimeSpan.FromHours(1);
// /// <summary>
// /// ctor
// /// </summary>
// public RestApiClientOptions()
// {
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseAddress">Base address for the API</param>
// public RestApiClientOptions(string baseAddress): base(baseAddress)
// {
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseOn">Copy values for the provided options</param>
// /// <param name="newValues">Copy values for the provided options</param>
// public RestApiClientOptions(RestApiClientOptions baseOn, RestApiClientOptions? newValues): base(baseOn, newValues)
// {
// HttpClient = newValues?.HttpClient ?? baseOn.HttpClient;
// RequestTimeout = newValues == default ? baseOn.RequestTimeout : newValues.RequestTimeout;
// RateLimitingBehaviour = newValues?.RateLimitingBehaviour ?? baseOn.RateLimitingBehaviour;
// AutoTimestamp = newValues?.AutoTimestamp ?? baseOn.AutoTimestamp;
// TimestampRecalculationInterval = newValues?.TimestampRecalculationInterval ?? baseOn.TimestampRecalculationInterval;
// RateLimiters = newValues?.RateLimiters.ToList() ?? baseOn?.RateLimiters.ToList() ?? new List<IRateLimiter>();
// }
// /// <inheritdoc />
// public override string ToString()
// {
// return $"{base.ToString()}, RequestTimeout: {RequestTimeout:c}, HttpClient: {(HttpClient == null ? "-" : "set")}, RateLimiters: {RateLimiters?.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, AutoTimestamp: {AutoTimestamp}, TimestampRecalculationInterval: {TimestampRecalculationInterval}";
// }
// }
// /// <summary>
// /// Rest API client options
// /// </summary>
// public class SocketApiClientOptions : ApiClientOptions
// {
// /// <summary>
// /// Whether or not the socket should automatically reconnect when losing connection
// /// </summary>
// public bool AutoReconnect { get; set; } = true;
// /// <summary>
// /// Time to wait between reconnect attempts
// /// </summary>
// public TimeSpan ReconnectInterval { get; set; } = TimeSpan.FromSeconds(5);
// /// <summary>
// /// Max number of concurrent resubscription tasks per socket after reconnecting a socket
// /// </summary>
// public int MaxConcurrentResubscriptionsPerSocket { get; set; } = 5;
// /// <summary>
// /// The max time to wait for a response after sending a request on the socket before giving a timeout
// /// </summary>
// public TimeSpan SocketResponseTimeout { get; set; } = TimeSpan.FromSeconds(10);
// /// <summary>
// /// The max time of not receiving any data after which the connection is assumed to be dropped. This can only be used for socket connections where a steady flow of data is expected,
// /// for example when the server sends intermittent ping requests
// /// </summary>
// public TimeSpan SocketNoDataTimeout { get; set; }
// /// <summary>
// /// The amount of subscriptions that should be made on a single socket connection. Not all API's support multiple subscriptions on a single socket.
// /// 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 issues.
// /// </summary>
// public int? SocketSubscriptionsCombineTarget { get; set; }
// /// <summary>
// /// The max amount of connections to make to the server. Can be used for API's which only allow a certain number of connections. Changing this to a high value might cause issues.
// /// </summary>
// public int? MaxSocketConnections { get; set; }
// /// <summary>
// /// The time to wait after connecting a socket before sending messages. Can be used for API's which will rate limit if you subscribe directly after connecting.
// /// </summary>
// public TimeSpan DelayAfterConnect { get; set; } = TimeSpan.Zero;
// /// <summary>
// /// ctor
// /// </summary>
// public SocketApiClientOptions()
// {
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseAddress">Base address for the API</param>
// public SocketApiClientOptions(string baseAddress) : base(baseAddress)
// {
// }
// /// <summary>
// /// ctor
// /// </summary>
// /// <param name="baseOptions">Copy values for the provided options</param>
// /// <param name="newValues">Copy values for the provided options</param>
// public SocketApiClientOptions(SocketApiClientOptions baseOptions, SocketApiClientOptions? newValues) : base(baseOptions, newValues)
// {
// if (baseOptions == null)
// return;
// AutoReconnect = newValues?.AutoReconnect ?? baseOptions.AutoReconnect;
// ReconnectInterval = newValues?.ReconnectInterval ?? baseOptions.ReconnectInterval;
// MaxConcurrentResubscriptionsPerSocket = newValues?.MaxConcurrentResubscriptionsPerSocket ?? baseOptions.MaxConcurrentResubscriptionsPerSocket;
// SocketResponseTimeout = newValues?.SocketResponseTimeout ?? baseOptions.SocketResponseTimeout;
// SocketNoDataTimeout = newValues?.SocketNoDataTimeout ?? baseOptions.SocketNoDataTimeout;
// SocketSubscriptionsCombineTarget = newValues?.SocketSubscriptionsCombineTarget ?? baseOptions.SocketSubscriptionsCombineTarget;
// MaxSocketConnections = newValues?.MaxSocketConnections ?? baseOptions.MaxSocketConnections;
// DelayAfterConnect = newValues?.DelayAfterConnect ?? baseOptions.DelayAfterConnect;
// }
// /// <inheritdoc />
// public override string ToString()
// {
// return $"{base.ToString()}, AutoReconnect: {AutoReconnect}, ReconnectInterval: {ReconnectInterval}, MaxConcurrentResubscriptionsPerSocket: {MaxConcurrentResubscriptionsPerSocket}, SocketResponseTimeout: {SocketResponseTimeout:c}, SocketNoDataTimeout: {SocketNoDataTimeout}, SocketSubscriptionsCombineTarget: {SocketSubscriptionsCombineTarget}, MaxSocketConnections: {MaxSocketConnections}";
// }
// }
// /// <summary>
// /// Base for order book options
// /// </summary>
// public class OrderBookOptions : ClientOptions
// {
// /// <summary>
// /// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
// /// </summary>
// public bool ChecksumValidationEnabled { get; set; } = true;
// }
//}
+12 -1
View File
@@ -9,8 +9,19 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public class TraceLoggerProvider : ILoggerProvider
{
private readonly LogLevel _logLevel;
/// <summary>
/// ctor
/// </summary>
/// <param name="logLevel"></param>
public TraceLoggerProvider(LogLevel? logLevel = null)
{
_logLevel = logLevel ?? LogLevel.Trace;
}
/// <inheritdoc />
public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName);
public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName, _logLevel);
/// <inheritdoc />
public void Dispose() { }
}
+9 -14
View File
@@ -5,20 +5,15 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Binance.Net" Version="8.0.6" />
<PackageReference Include="Bitfinex.Net" Version="5.0.3" />
<PackageReference Include="Bittrex.Net" Version="7.0.4" />
<PackageReference Include="Bybit.Net" Version="0.0.4" />
<PackageReference Include="CoinEx.Net" Version="5.0.3" />
<PackageReference Include="FTX.Net" Version="1.0.4" />
<PackageReference Include="Huobi.Net" Version="4.0.4" />
<PackageReference Include="KrakenExchange.Net" Version="3.0.3" />
<PackageReference Include="Kucoin.Net" Version="4.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.1-dev-00250" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\" />
<PackageReference Include="Binance.Net" Version="9.0.1" />
<PackageReference Include="Bitfinex.Net" Version="6.0.0" />
<PackageReference Include="Bittrex.Net" Version="8.0.0" />
<PackageReference Include="Bybit.Net" Version="3.0.0" />
<PackageReference Include="CoinEx.Net" Version="6.0.0" />
<PackageReference Include="Huobi.Net" Version="5.0.0" />
<PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
<PackageReference Include="Kucoin.Net" Version="5.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
</ItemGroup>
</Project>
+11 -16
View File
@@ -1,13 +1,12 @@
@page "/"
@inject IBinanceClient binanceClient
@inject IBitfinexClient bitfinexClient
@inject IBittrexClient bittrexClient
@inject IBybitClient bybitClient
@inject ICoinExClient coinexClient
@inject IFTXClient ftxClient
@inject IHuobiClient huobiClient
@inject IKrakenClient krakenClient
@inject IKucoinClient kucoinClient
@inject IBinanceRestClient binanceClient
@inject IBitfinexRestClient bitfinexClient
@inject IBittrexRestClient bittrexClient
@inject IBybitRestClient bybitClient
@inject ICoinExRestClient coinexClient
@inject IHuobiRestClient huobiClient
@inject IKrakenRestClient krakenClient
@inject IKucoinRestClient kucoinClient
<h3>BTC-USD prices:</h3>
@foreach(var price in _prices.OrderBy(p => p.Key))
@@ -23,14 +22,13 @@
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
var bittrexTask = bittrexClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
var bybitTask = bybitClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var bybitTask = bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
var coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var ftxTask = ftxClient.TradeApi.ExchangeData.GetSymbolAsync("BTC/USD");
var huobiTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
await Task.WhenAll(binanceTask, bitfinexTask, bittrexTask, bybitTask, coinexTask, ftxTask, huobiTask, krakenTask, kucoinTask);
await Task.WhenAll(binanceTask, bitfinexTask, bittrexTask, bybitTask, coinexTask, huobiTask, krakenTask, kucoinTask);
if (binanceTask.Result.Success)
_prices.Add("Binance", binanceTask.Result.Data.LastPrice);
@@ -42,14 +40,11 @@
_prices.Add("Bittrex", bittrexTask.Result.Data.LastPrice);
if (bybitTask.Result.Success)
_prices.Add("Bybit", bybitTask.Result.Data.LastPrice);
_prices.Add("Bybit", bybitTask.Result.Data.List.First().LastPrice);
if (coinexTask.Result.Success)
_prices.Add("CoinEx", coinexTask.Result.Data.Ticker.LastPrice);
if (ftxTask.Result.Success)
_prices.Add("FTX", ftxTask.Result.Data.LastPrice ?? 0);
if (huobiTask.Result.Success)
_prices.Add("Huobi", huobiTask.Result.Data.ClosePrice ?? 0);
+9 -19
View File
@@ -4,21 +4,12 @@
@inject IBittrexSocketClient bittrexSocketClient
@inject IBybitSocketClient bybitSocketClient
@inject ICoinExSocketClient coinExSocketClient
@inject IFTXSocketClient ftxSocketClient
@inject IHuobiSocketClient huobiSocketClient
@inject IKrakenSocketClient krakenSocketClient
@inject IKucoinSocketClient kucoinSocketClient
@using Binance.Net.Clients.SpotApi
@using Bitfinex.Net.Clients.SpotApi
@using Bittrex.Net.Clients.SpotApi
@using Bybit.Net.Clients.SpotApi
@using CoinEx.Net.Clients.SpotApi
@using System.Collections.Concurrent
@using CryptoExchange.Net.Objects
@using CryptoExchange.Net.Sockets
@using Huobi.Net.Clients.SpotApi
@using Kraken.Net.Clients.SpotApi
@using Kucoin.Net.Clients.SpotApi
@using System.Collections.Concurrent
@implements IDisposable
<h3>ETH-BTC prices, live updates:</h3>
@@ -35,15 +26,14 @@
{
var tasks = new Task<CallResult<UpdateSubscription>>[]
{
binanceSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
bitfinexSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
bittrexSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", data.Data.LastPrice)),
bybitSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
coinExSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)),
ftxSocketClient.Streams.SubscribeToTickerUpdatesAsync("ETH/BTC", data => UpdateData("FTX", data.Data.LastPrice ?? 0)),
huobiSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)),
krakenSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)),
kucoinSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
bittrexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", data.Data.LastPrice)),
bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
coinExSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)),
huobiSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)),
krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)),
kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
};
await Task.WhenAll(tasks);
+31 -27
View File
@@ -1,19 +1,24 @@
@page "/OrderBooks"
@using Binance.Net.SymbolOrderBooks
@using Bitfinex.Net.SymbolOrderBooks
@using Bittrex.Net.SymbolOrderBooks
@using Bybit.Net.SymbolOrderBooks
@using CryptoExchange.Net.Interfaces
@using CryptoExchange.Net.Objects
@using CryptoExchange.Net.Sockets
@using CoinEx.Net.SymbolOrderBooks
@using FTX.Net.SymbolOrderBooks
@using Huobi.Net.SymbolOrderBooks
@using Kraken.Net.SymbolOrderBooks
@using Kucoin.Net.Clients
@using Kucoin.Net.SymbolOrderBooks
@using System.Collections.Concurrent
@using System.Timers
@using Binance.Net.Interfaces
@using Bitfinex.Net.Interfaces
@using Bittrex.Net.Interfaces
@using Bybit.Net.Interfaces
@using CoinEx.Net.Interfaces
@using CryptoExchange.Net.Interfaces
@using Huobi.Net.Interfaces
@using Kraken.Net.Interfaces
@using Kucoin.Net.Clients
@using Kucoin.Net.Interfaces
@inject IBinanceOrderBookFactory binanceFactory
@inject IBitfinexOrderBookFactory bitfinexFactory
@inject IBittrexOrderBookFactory bittrexFactory
@inject IBybitOrderBookFactory bybitFactory
@inject ICoinExOrderBookFactory coinExFactory
@inject IHuobiOrderBookFactory huobiFactory
@inject IKrakenOrderBookFactory krakenFactory
@inject IKucoinOrderBookFactory kucoinFactory
@implements IDisposable
<h3>ETH-BTC books, live updates:</h3>
@@ -40,22 +45,21 @@
protected override async Task OnInitializedAsync()
{
// Since the Kucoin order book stream needs authentication we will need to provide API credentials beforehand
KucoinClient.SetDefaultOptions(new Kucoin.Net.Objects.KucoinClientOptions
{
ApiCredentials = new Kucoin.Net.Objects.KucoinApiCredentials("KEY", "SECRET", "PASSPHRASE")
});
KucoinRestClient.SetDefaultOptions(options =>
{
options.ApiCredentials = new Kucoin.Net.Objects.KucoinApiCredentials("KEY", "SECRET", "PASSPHRASE");
});
_books = new Dictionary<string, ISymbolOrderBook>
{
{ "Binance", new BinanceSpotSymbolOrderBook("ETHBTC") },
{ "Bitfinex", new BitfinexSymbolOrderBook("tETHBTC") },
{ "Bittrex", new BittrexSymbolOrderBook("ETH-BTC") },
{ "Bybit", new BybitSpotSymbolOrderBook("ETHBTC") },
{ "CoinEx", new CoinExSpotSymbolOrderBook("ETHBTC") },
{ "FTX", new FTXSymbolOrderBook("ETH/BTC") },
{ "Huobi", new HuobiSpotSymbolOrderBook("ethbtc") },
{ "Kraken", new KrakenSpotSymbolOrderBook("ETH/XBT") },
{ "Kucoin", new KucoinSpotSymbolOrderBook("ETH-BTC") },
{ "Binance", binanceFactory.CreateSpot("ETHBTC") },
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") },
{ "Bittrex", bittrexFactory.Create("ETH-BTC") },
{ "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) },
{ "CoinEx", coinExFactory.CreateSpot("ETHBTC") },
{ "Huobi", huobiFactory.CreateSpot("ethbtc") },
{ "Kraken", krakenFactory.CreateSpot("ETH/XBT") },
{ "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
};
await Task.WhenAll(_books.Select(b => b.Value.StartAsync()));
@@ -70,7 +74,7 @@
{
_timer.Stop();
_timer.Dispose();
foreach (var book in _books)
foreach (var book in _books.Where(b => b.Value.Status != CryptoExchange.Net.Objects.OrderBookStatus.Disconnected))
// It's not necessary to wait for this
_ = book.Value.StopAsync();
}
+10 -13
View File
@@ -1,13 +1,12 @@
@page "/SpotClient"
@inject IBinanceClient binanceClient
@inject IBitfinexClient bitfinexClient
@inject IBittrexClient bittrexClient
@inject IBybitClient bybitClient
@inject ICoinExClient coinexClient
@inject IFTXClient ftxClient
@inject IHuobiClient huobiClient
@inject IKrakenClient krakenClient
@inject IKucoinClient kucoinClient
@inject IBinanceRestClient binanceClient
@inject IBitfinexRestClient bitfinexClient
@inject IBittrexRestClient bittrexClient
@inject IBybitRestClient bybitClient
@inject ICoinExRestClient coinexClient
@inject IHuobiRestClient huobiClient
@inject IKrakenRestClient krakenClient
@inject IKucoinRestClient kucoinClient
@using Binance.Net.Clients.SpotApi
@using Bitfinex.Net.Clients.SpotApi
@using Bittrex.Net.Clients.SpotApi
@@ -15,7 +14,6 @@
@using CoinEx.Net.Clients.SpotApi
@using CryptoExchange.Net.Interfaces
@using CryptoExchange.Net.Interfaces.CommonClients
@using FTX.Net.Clients.TradeApi
@using Huobi.Net.Clients.SpotApi
@using Kraken.Net.Clients.SpotApi
@using Kucoin.Net.Clients.SpotApi
@@ -37,9 +35,8 @@
binanceClient.SpotApi.CommonSpotClient,
bitfinexClient.SpotApi.CommonSpotClient,
bittrexClient.SpotApi.CommonSpotClient,
bybitClient.SpotApi.CommonSpotClient,
coinexClient.SpotApi.CommonSpotClient,
ftxClient.TradeApi.CommonSpotClient,
bybitClient.SpotApiV1.CommonSpotClient,
coinexClient.SpotApi.CommonSpotClient,
huobiClient.SpotApi.CommonSpotClient,
krakenClient.SpotApi.CommonSpotClient,
kucoinClient.SpotApi.CommonSpotClient
+4 -25
View File
@@ -2,15 +2,11 @@ using System.Collections.Generic;
using Binance.Net;
using Binance.Net.Clients;
using Binance.Net.Interfaces.Clients;
using Binance.Net.Objects;
using Bitfinex.Net;
using Bittrex.Net;
using Bybit.Net;
using CoinEx.Net;
using CoinEx.Net.Clients;
using CoinEx.Net.Interfaces.Clients;
using CryptoExchange.Net.Authentication;
using FTX.Net;
using Huobi.Net;
using Kraken.Net;
using Kucoin.Net;
@@ -43,35 +39,18 @@ namespace BlazorClient
services.AddServerSideBlazor();
// Register the clients, options can be provided in the callback parameter
services.AddBinance((restClientOptions, socketClientOptions) => {
restClientOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
restClientOptions.LogLevel = LogLevel.Trace;
// Point the logging to use the ILogger configuration, which uses Serilog here
restClientOptions.LogWriters = new List<ILogger> { _loggerFactory.CreateLogger<IBinanceClient>() };
socketClientOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
});
BinanceClient.SetDefaultOptions(new BinanceClientOptions
services.AddBinance(restOptions =>
{
ApiCredentials = new ApiCredentials("KEY", "SECRET"),
LogLevel = LogLevel.Trace
});
BinanceSocketClient.SetDefaultOptions(new BinanceSocketClientOptions
restOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
}, socketOptions =>
{
ApiCredentials = new ApiCredentials("KEY", "SECRET"),
socketOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
});
services.AddTransient<IBinanceClient, BinanceClient>();
services.AddScoped<IBinanceSocketClient, BinanceSocketClient>();
services.AddBitfinex();
services.AddBittrex();
services.AddBybit();
services.AddCoinEx();
services.AddFTX();
services.AddHuobi();
services.AddKraken();
services.AddKucoin();
-1
View File
@@ -13,7 +13,6 @@
@using Bittrex.Net.Interfaces.Clients;
@using Bybit.Net.Interfaces.Clients;
@using CoinEx.Net.Interfaces.Clients;
@using FTX.Net.Interfaces.Clients;
@using Huobi.Net.Interfaces.Clients;
@using Kraken.Net.Interfaces.Clients;
@using Kucoin.Net.Interfaces.Clients;
+8 -9
View File
@@ -6,15 +6,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Binance.Net" Version="8.0.6" />
<PackageReference Include="Bitfinex.Net" Version="5.0.3" />
<PackageReference Include="Bittrex.Net" Version="7.0.4" />
<PackageReference Include="Bybit.Net" Version="0.0.4" />
<PackageReference Include="CoinEx.Net" Version="5.0.3" />
<PackageReference Include="FTX.Net" Version="1.0.4" />
<PackageReference Include="Huobi.Net" Version="4.0.4" />
<PackageReference Include="KrakenExchange.Net" Version="3.0.3" />
<PackageReference Include="Kucoin.Net" Version="4.0.3" />
<PackageReference Include="Binance.Net" Version="9.0.1" />
<PackageReference Include="Bitfinex.Net" Version="6.0.0" />
<PackageReference Include="Bittrex.Net" Version="8.0.0" />
<PackageReference Include="Bybit.Net" Version="3.0.0" />
<PackageReference Include="CoinEx.Net" Version="6.0.0" />
<PackageReference Include="Huobi.Net" Version="5.0.0" />
<PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
<PackageReference Include="Kucoin.Net" Version="5.0.0" />
</ItemGroup>
</Project>
@@ -17,21 +17,21 @@ namespace ConsoleClient.Exchanges
public async Task<WebCallResult> CancelOrder(string symbol, string id)
{
using var client = new BinanceClient();
using var client = new BinanceRestClient();
var result = await client.SpotApi.Trading.CancelOrderAsync(symbol, long.Parse(id));
return result.AsDataless();
}
public async Task<Dictionary<string, decimal>> GetBalances()
{
using var client = new BinanceClient();
using var client = new BinanceRestClient();
var result = await client.SpotApi.Account.GetAccountInfoAsync();
return result.Data.Balances.ToDictionary(b => b.Asset, b => b.Total);
}
public async Task<IEnumerable<OpenOrder>> GetOpenOrders()
{
using var client = new BinanceClient();
using var client = new BinanceRestClient();
var result = await client.SpotApi.Trading.GetOpenOrdersAsync();
// Should check result success status here
return result.Data.Select(o => new OpenOrder
@@ -49,7 +49,7 @@ namespace ConsoleClient.Exchanges
public async Task<decimal> GetPrice(string symbol)
{
using var client = new BinanceClient();
using var client = new BinanceRestClient();
var result = await client.SpotApi.ExchangeData.GetPriceAsync(symbol);
// Should check result success status here
return result.Data.Price;
@@ -57,7 +57,7 @@ namespace ConsoleClient.Exchanges
public async Task<WebCallResult<string>> PlaceOrder(string symbol, string side, string type, decimal quantity, decimal? price)
{
using var client = new BinanceClient();
using var client = new BinanceRestClient();
var result = await client.SpotApi.Trading.PlaceOrderAsync(
symbol,
side.ToLower() == "buy" ? Binance.Net.Enums.OrderSide.Buy: Binance.Net.Enums.OrderSide.Sell,
@@ -70,7 +70,7 @@ namespace ConsoleClient.Exchanges
public async Task<UpdateSubscription> SubscribePrice(string symbol, Action<decimal> handler)
{
var sub = await _socketClient.SpotStreams.SubscribeToMiniTickerUpdatesAsync(symbol, data => handler(data.Data.LastPrice));
var sub = await _socketClient.SpotApi.ExchangeData.SubscribeToMiniTickerUpdatesAsync(symbol, data => handler(data.Data.LastPrice));
return sub.Data;
}
}
@@ -0,0 +1,75 @@
using Bybit.Net.Clients;
using Bybit.Net.Interfaces.Clients;
using ConsoleClient.Models;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleClient.Exchanges
{
internal class BybitExchange : IExchange
{
private IBybitSocketClient _socketClient = new BybitSocketClient();
public async Task<WebCallResult> CancelOrder(string symbol, string id)
{
using var client = new BybitRestClient();
var result = await client.V5Api.Trading.CancelOrderAsync(Bybit.Net.Enums.Category.Spot, symbol, id);
return result.AsDataless();
}
public async Task<Dictionary<string, decimal>> GetBalances()
{
using var client = new BybitRestClient();
var result = await client.V5Api.Account.GetBalancesAsync(Bybit.Net.Enums.AccountType.Spot);
return result.Data.List.First().Assets.ToDictionary(d => d.Asset, d => d.WalletBalance);
}
public async Task<IEnumerable<OpenOrder>> GetOpenOrders()
{
using var client = new BybitRestClient();
var order = await client.V5Api.Trading.GetOrdersAsync(Bybit.Net.Enums.Category.Spot);
return order.Data.List.Select(o => new OpenOrder
{
Symbol = o.Symbol,
OrderSide = o.Side.ToString(),
OrderStatus = o.Status.ToString(),
OrderTime = o.CreateTime,
OrderType = o.OrderType.ToString(),
Price = o.Price ?? 0,
Quantity = o.Quantity,
QuantityFilled = o.QuantityFilled ?? 0
});
}
public async Task<decimal> GetPrice(string symbol)
{
using var client = new BybitRestClient();
var result = await client.V5Api.ExchangeData.GetSpotTickersAsync(symbol);
return result.Data.List.First().LastPrice;
}
public async Task<WebCallResult<string>> PlaceOrder(string symbol, string side, string type, decimal quantity, decimal? price)
{
using var client = new BybitRestClient();
var result = await client.V5Api.Trading.PlaceOrderAsync(
Bybit.Net.Enums.Category.Spot,
symbol,
side.ToLower() == "buy" ? Bybit.Net.Enums.OrderSide.Buy : Bybit.Net.Enums.OrderSide.Sell,
type == "market" ? Bybit.Net.Enums.NewOrderType.Market : Bybit.Net.Enums.NewOrderType.Limit,
quantity,
price: price);
return result.As(result.Data?.OrderId.ToString());
}
public async Task<UpdateSubscription> SubscribePrice(string symbol, Action<decimal> handler)
{
var sub = await _socketClient.V5SpotApi.SubscribeToTickerUpdatesAsync(symbol, data => handler(data.Data.LastPrice));
return sub.Data;
}
}
}
@@ -1,74 +0,0 @@
using ConsoleClient.Models;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Sockets;
using FTX.Net.Clients;
using FTX.Net.Interfaces.Clients;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleClient.Exchanges
{
internal class FTXExchange : IExchange
{
private IFTXSocketClient _socketClient = new FTXSocketClient();
public async Task<WebCallResult> CancelOrder(string symbol, string id)
{
using var client = new FTXClient();
var result = await client.TradeApi.Trading.CancelOrderAsync(long.Parse(id));
return result.AsDataless();
}
public async Task<Dictionary<string, decimal>> GetBalances()
{
using var client = new FTXClient();
var result = await client.TradeApi.Account.GetBalancesAsync();
return result.Data.ToDictionary(d => d.Asset, d => d.Total);
}
public async Task<IEnumerable<OpenOrder>> GetOpenOrders()
{
using var client = new FTXClient();
var order = await client.TradeApi.Trading.GetOpenOrdersAsync();
return order.Data.Select(o => new OpenOrder
{
Symbol = o.Symbol,
OrderSide = o.Side.ToString(),
OrderStatus = o.Status.ToString(),
OrderTime = o.CreateTime,
OrderType = o.Type.ToString(),
Price = o.Price ?? 0,
Quantity = o.Quantity,
QuantityFilled = o.QuantityFilled ?? 0
});
}
public async Task<decimal> GetPrice(string symbol)
{
using var client = new FTXClient();
var result = await client.TradeApi.ExchangeData.GetSymbolAsync(symbol);
return result.Data.LastPrice ?? 0;
}
public async Task<WebCallResult<string>> PlaceOrder(string symbol, string side, string type, decimal quantity, decimal? price)
{
using var client = new FTXClient();
var result = await client.TradeApi.Trading.PlaceOrderAsync(
symbol,
side.ToLower() == "buy" ? FTX.Net.Enums.OrderSide.Buy : FTX.Net.Enums.OrderSide.Sell,
type == "market" ? FTX.Net.Enums.OrderType.Market : FTX.Net.Enums.OrderType.Limit,
quantity,
price: price);
return result.As(result.Data?.Id.ToString());
}
public async Task<UpdateSubscription> SubscribePrice(string symbol, Action<decimal> handler)
{
var sub = await _socketClient.Streams.SubscribeToTickerUpdatesAsync(symbol, data => handler(data.Data.LastPrice ?? 0));
return sub.Data;
}
}
}
+6 -10
View File
@@ -5,12 +5,10 @@ using System.Linq;
using System.Threading.Tasks;
using Binance.Net.Clients;
using Binance.Net.Objects;
using Bybit.Net.Clients;
using ConsoleClient.Exchanges;
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Sockets;
using FTX.Net.Clients;
using FTX.Net.Objects;
using Microsoft.Extensions.Logging;
namespace ConsoleClient
{
@@ -19,20 +17,18 @@ namespace ConsoleClient
static Dictionary<string, IExchange> _exchanges = new Dictionary<string, IExchange>
{
{ "Binance", new BinanceExchange() },
{ "FTX", new FTXExchange() }
{ "Bybit", new BybitExchange() }
};
static async Task Main(string[] args)
{
BinanceClient.SetDefaultOptions(new BinanceClientOptions
BinanceRestClient.SetDefaultOptions(options =>
{
LogLevel = LogLevel.Trace,
ApiCredentials = new ApiCredentials("APIKEY", "APISECRET")
options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
});
FTXClient.SetDefaultOptions(new FTXClientOptions
BybitRestClient.SetDefaultOptions(options =>
{
LogLevel = LogLevel.Trace,
ApiCredentials = new ApiCredentials("APIKEY", "APISECRET")
options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
});
while (true)
+3
View File
@@ -33,6 +33,9 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
## Release notes
* Version 6.0.1 - 29 Jun 2023
* Added LogLevel optional parameter to TraceLoggerProvider
* Version 6.0.0 - 25 Jun 2023
* Updated ApiCredentials to support RSA signing as well as the default Hmac signature
* Removed custom logging implementation in favor of using `Microsoft.Extensions.Logging` ILogger directly
+19 -19
View File
@@ -11,15 +11,15 @@ Each implementation generally provides two different clients, which will be the
The rest client gives access to the Rest endpoint of the API. Rest endpoints are accessed by sending an HTTP request and receiving a response. The client is split in different sub-clients, which are named API Clients. These API clients are then again split in different topics. Typically a Rest client will look like this:
- [ExchangeName]RestClient
- SpotApi
- Account
- ExchangeData
- Trading
- FuturesApi
- Account
- ExchangeData
- Trading
- SpotApi
- Account
- ExchangeData
- Trading
- FuturesApi
- Account
- ExchangeData
- Trading
This rest client has 2 different API clients, the `SpotApi` and the `FuturesApi`, each offering their own set of endpoints.
*Requesting ticker info on the spot API*
@@ -64,8 +64,8 @@ When processing the result of a call it should always be checked for success. No
var callResult = await kucoinClient.SpotApi.ExchangeData.GetTickersAsync();
if(!callResult.Success)
{
Console.WriteLine("Request failed: " + callResult.Error);
return;
Console.WriteLine("Request failed: " + callResult.Error);
return;
}
Console.WriteLine("Result: " + callResult.Data);
@@ -78,8 +78,8 @@ Just like the Rest client is divided in Rest Api clients, the Socket client is d
```csharp
- KucoinSocketClient
- SpotStreams
- FuturesStreams
- SpotStreams
- FuturesStreams
```
*Subscribing to updates for all tickers on the Spot Api*
@@ -95,7 +95,7 @@ await kucoinSocketClient.SpotStreams.SubscribeToAllTickerUpdatesAsync(DataHandle
private static void DataHandler(DataEvent<KucoinStreamTick> updateData)
{
// Process updateData
// Process updateData
}
```
@@ -103,7 +103,7 @@ private static void DataHandler(DataEvent<KucoinStreamTick> updateData)
```csharp
await kucoinSocketClient.SpotStreams.SubscribeToAllTickerUpdatesAsync(updateData =>
{
// Process updateData
// Process updateData
});
```
@@ -122,16 +122,16 @@ Subscribing to a stream will return a `CallResult<UpdateSubscription>` object. T
var subscriptionResult = await kucoinSocketClient.SpotStreams.SubscribeToAllTickerUpdatesAsync(DataHandler);
if(!subscriptionResult.Success)
{
Console.WriteLine("Failed to connect: " + subscriptionResult.Error);
return;
Console.WriteLine("Failed to connect: " + subscriptionResult.Error);
return;
}
subscriptionResult.Data.ConnectionLost += () =>
{
Console.WriteLine("Connection lost");
Console.WriteLine("Connection lost");
};
subscriptionResult.Data.ConnectionRestored += (time) =>
{
Console.WriteLine("Connection restored");
Console.WriteLine("Connection restored");
};
```
+3 -3
View File
@@ -28,7 +28,7 @@ private void SomeMethod()
{
var socketClient = new BinanceSocketClient();
socketClient.Spot.SubscribeToOrderBookUpdates("BTCUSDT", data => {
// Handle data
// Handle data
});
}
```
@@ -41,7 +41,7 @@ private BinanceSocketClient _socketClient = new BinanceSocketClient();
private void SomeMethod()
{
_socketClient.Spot.SubscribeToOrderBookUpdates("BTCUSDT", data => {
// Handle data
// Handle data
});
}
@@ -52,7 +52,7 @@ Yes, generally these are all supported and can be configured by setting the Envi
```csharp
var client = new BinanceRestClient(options =>
{
options.Environment = BinanceEnvironment.Testnet;
options.Environment = BinanceEnvironment.Testnet;
});
```
+5 -5
View File
@@ -10,7 +10,7 @@ The library offers extensive logging, which depends on the dotnet `Microsoft.Ext
```csharp
IServiceCollection services = new ServiceCollection();
services
.AddBinance()
.AddBinance()
.AddLogging(options =>
{
options.SetMinimumLevel(LogLevel.Trace);
@@ -66,8 +66,8 @@ IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddBinance();
serviceCollection.AddLogging(options =>
{
options.SetMinimumLevel(LogLevel.Trace);
options.AddConsole();
options.SetMinimumLevel(LogLevel.Trace);
options.AddConsole();
}).BuildServiceProvider();
var client = serviceCollection.GetRequiredService<IBinanceRestClient>();
@@ -91,7 +91,7 @@ By default the `OriginalData` property in the `WebCallResult`/`DataEvent` object
```csharp
var client = new BinanceClient(options =>
{
options.OutputOriginalData = true
options.OutputOriginalData = true
});
```
@@ -103,7 +103,7 @@ var originallyReceivedData = tickerResult.OriginalData;
// Socket update
await client.SpotStreams.SubscribeToAllTickerUpdatesAsync(update => {
var originallyRecievedData = update.OriginalData;
var originallyRecievedData = update.OriginalData;
});
```
+8 -8
View File
@@ -27,9 +27,9 @@ The HttpClient will now be received by the DI container instead of having to pas
```csharp
var client = new BinanceClient(new BinanceClientOptions(){
OutputOriginalData = true,
SpotApiOptions = new RestApiOptions {
BaseAddress = BinanceApiAddresses.TestNet.RestClientAddress
}
SpotApiOptions = new RestApiOptions {
BaseAddress = BinanceApiAddresses.TestNet.RestClientAddress
}
// Other options
});
```
@@ -38,7 +38,7 @@ var client = new BinanceClient(new BinanceClientOptions(){
```csharp
var client = new BinanceClient(options => {
options.OutputOriginalData = true;
options.Environment = BinanceEnvironment.Testnet;
options.Environment = BinanceEnvironment.Testnet;
// Other options
});
```
@@ -53,10 +53,10 @@ With the change in options providing the DI extension methods for the IServiceCo
```csharp
builder.Services.AddKucoin((restOpts, socketOpts) =>
{
restOpts.LogLevel = LogLevel.Debug;
restOpts.ApiCredentials = new KucoinApiCredentials("KEY", "SECRET", "PASS");
socketOpts.LogLevel = LogLevel.Debug;
socketOpts.ApiCredentials = new KucoinApiCredentials("KEY", "SECRET", "PASS");
restOpts.LogLevel = LogLevel.Debug;
restOpts.ApiCredentials = new KucoinApiCredentials("KEY", "SECRET", "PASS");
socketOpts.LogLevel = LogLevel.Debug;
socketOpts.ApiCredentials = new KucoinApiCredentials("KEY", "SECRET", "PASS");
}, ServiceLifetime.Singleton);
```
+8 -8
View File
@@ -12,8 +12,8 @@ Each implementation can be configured using client options. There are 2 ways to
BinanceClient.SetDefaultOptions(options =>
{
options.OutputOriginalData = true;
options.ApiCredentials = new ApiCredentials("KEY", "SECRET");
options.OutputOriginalData = true;
options.ApiCredentials = new ApiCredentials("KEY", "SECRET");
});
```
@@ -23,8 +23,8 @@ BinanceClient.SetDefaultOptions(options =>
var client = new BinanceClient(options =>
{
options.OutputOriginalData = true;
options.ApiCredentials = new ApiCredentials("KEY", "SECRET");
options.OutputOriginalData = true;
options.ApiCredentials = new ApiCredentials("KEY", "SECRET");
});
```
@@ -34,12 +34,12 @@ When calling `SetDefaultOptions` each client created after that will use the opt
BinanceClient.SetDefaultOptions(options =>
{
options.OutputOriginalData = true;
options.OutputOriginalData = true;
});
var client = new BinanceClient(options =>
{
options.OutputOriginalData = false;
options.OutputOriginalData = false;
});
```
@@ -54,8 +54,8 @@ The options are divided in two categories. The basic options, which will apply t
var client = new BinanceRestClient(options =>
{
options.ApiCredentials = new ApiCredentials("GENERAL-KEY", "GENERAL-SECRET"),
options.SpotOptions.ApiCredentials = new ApiCredentials("SPOT-KEY", "SPOT-SECRET");
options.ApiCredentials = new ApiCredentials("GENERAL-KEY", "GENERAL-SECRET"),
options.SpotOptions.ApiCredentials = new ApiCredentials("SPOT-KEY", "SPOT-SECRET");
});
```
+5 -5
View File
@@ -18,15 +18,15 @@ book.OnStatusChange += (oldState, newState) => Console.WriteLine($"State changed
var startResult = await book.StartAsync();
if (!startResult.Success)
{
Console.WriteLine("Failed to start order book: " + startResult.Error);
return;
Console.WriteLine("Failed to start order book: " + startResult.Error);
return;
}
while(true)
{
Console.Clear();
Console.WriteLine(book.ToString(3);
await Task.Delay(500);
Console.Clear();
Console.WriteLine(book.ToString(3);
await Task.Delay(500);
}
```
+8 -8
View File
@@ -13,12 +13,12 @@ A rate limiter can be configured in the options like so:
```csharp
new ClientOptions
{
RateLimitingBehaviour = RateLimitingBehaviour.Wait,
RateLimiters = new List<IRateLimiter>
{
new RateLimiter()
.AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
}
RateLimitingBehaviour = RateLimitingBehaviour.Wait,
RateLimiters = new List<IRateLimiter>
{
new RateLimiter()
.AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
}
}
```
@@ -26,8 +26,8 @@ This will add a rate limiter for 50 requests per 10 seconds.
A rate limiter can have multiple limits:
```csharp
new RateLimiter()
.AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
.AddEndpointLimit("/api/order", 10, TimeSpan.FromSeconds(2))
.AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
.AddEndpointLimit("/api/order", 10, TimeSpan.FromSeconds(2))
```
This adds another limit of 10 requests per 2 seconds in addition to the 50 requests per 10 seconds limit.
These are the available rate limit configurations: