1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-15 18:33:06 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
JKorf 5048aea722 Updated version 2023-07-05 21:58:35 +02:00
Jkorf c3316a51e7 Added properties dictionary to socket connection 2023-07-05 17:10:43 +02:00
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
24 changed files with 253 additions and 604 deletions
+4 -4
View File
@@ -6,16 +6,16 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>A base package for implementing cryptocurrency API's</Description> <Description>A base package for implementing cryptocurrency API's</Description>
<PackageVersion>6.0.0</PackageVersion> <PackageVersion>6.0.2</PackageVersion>
<AssemblyVersion>6.0.0</AssemblyVersion> <AssemblyVersion>6.0.2</AssemblyVersion>
<FileVersion>6.0.0</FileVersion> <FileVersion>6.0.2</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl> <RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <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.2 - Added properties generic dictionary to SocketConnection</PackageReleaseNotes>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion> <LangVersion>10.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <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> /// </summary>
public class TraceLoggerProvider : ILoggerProvider 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 /> /// <inheritdoc />
public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName); public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName, _logLevel);
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() { } public void Dispose() { }
} }
@@ -108,6 +108,11 @@ namespace CryptoExchange.Net.Sockets
/// </summary> /// </summary>
public string Tag { get; set; } public string Tag { get; set; }
/// <summary>
/// Additional properties for this connection
/// </summary>
public Dictionary<string, object> Properties { get; set; }
/// <summary> /// <summary>
/// If activity is paused /// If activity is paused
/// </summary> /// </summary>
@@ -170,6 +175,7 @@ namespace CryptoExchange.Net.Sockets
_logger = logger; _logger = logger;
ApiClient = apiClient; ApiClient = apiClient;
Tag = tag; Tag = tag;
Properties = new Dictionary<string, object>();
_pendingRequests = new List<PendingRequest>(); _pendingRequests = new List<PendingRequest>();
_subscriptions = new List<SocketSubscription>(); _subscriptions = new List<SocketSubscription>();
+9 -14
View File
@@ -5,20 +5,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="8.0.6" /> <PackageReference Include="Binance.Net" Version="9.0.1" />
<PackageReference Include="Bitfinex.Net" Version="5.0.3" /> <PackageReference Include="Bitfinex.Net" Version="6.0.0" />
<PackageReference Include="Bittrex.Net" Version="7.0.4" /> <PackageReference Include="Bittrex.Net" Version="8.0.0" />
<PackageReference Include="Bybit.Net" Version="0.0.4" /> <PackageReference Include="Bybit.Net" Version="3.0.0" />
<PackageReference Include="CoinEx.Net" Version="5.0.3" /> <PackageReference Include="CoinEx.Net" Version="6.0.0" />
<PackageReference Include="FTX.Net" Version="1.0.4" /> <PackageReference Include="Huobi.Net" Version="5.0.0" />
<PackageReference Include="Huobi.Net" Version="4.0.4" /> <PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
<PackageReference Include="KrakenExchange.Net" Version="3.0.3" /> <PackageReference Include="Kucoin.Net" Version="5.0.0" />
<PackageReference Include="Kucoin.Net" Version="4.0.3" /> <PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.1-dev-00250" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+11 -16
View File
@@ -1,13 +1,12 @@
@page "/" @page "/"
@inject IBinanceClient binanceClient @inject IBinanceRestClient binanceClient
@inject IBitfinexClient bitfinexClient @inject IBitfinexRestClient bitfinexClient
@inject IBittrexClient bittrexClient @inject IBittrexRestClient bittrexClient
@inject IBybitClient bybitClient @inject IBybitRestClient bybitClient
@inject ICoinExClient coinexClient @inject ICoinExRestClient coinexClient
@inject IFTXClient ftxClient @inject IHuobiRestClient huobiClient
@inject IHuobiClient huobiClient @inject IKrakenRestClient krakenClient
@inject IKrakenClient krakenClient @inject IKucoinRestClient kucoinClient
@inject IKucoinClient kucoinClient
<h3>BTC-USD prices:</h3> <h3>BTC-USD prices:</h3>
@foreach(var price in _prices.OrderBy(p => p.Key)) @foreach(var price in _prices.OrderBy(p => p.Key))
@@ -23,14 +22,13 @@
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT"); var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD"); var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
var bittrexTask = bittrexClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT"); 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 coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
var ftxTask = ftxClient.TradeApi.ExchangeData.GetSymbolAsync("BTC/USD");
var huobiTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt"); var huobiTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD"); var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT"); 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) if (binanceTask.Result.Success)
_prices.Add("Binance", binanceTask.Result.Data.LastPrice); _prices.Add("Binance", binanceTask.Result.Data.LastPrice);
@@ -42,14 +40,11 @@
_prices.Add("Bittrex", bittrexTask.Result.Data.LastPrice); _prices.Add("Bittrex", bittrexTask.Result.Data.LastPrice);
if (bybitTask.Result.Success) if (bybitTask.Result.Success)
_prices.Add("Bybit", bybitTask.Result.Data.LastPrice); _prices.Add("Bybit", bybitTask.Result.Data.List.First().LastPrice);
if (coinexTask.Result.Success) if (coinexTask.Result.Success)
_prices.Add("CoinEx", coinexTask.Result.Data.Ticker.LastPrice); _prices.Add("CoinEx", coinexTask.Result.Data.Ticker.LastPrice);
if (ftxTask.Result.Success)
_prices.Add("FTX", ftxTask.Result.Data.LastPrice ?? 0);
if (huobiTask.Result.Success) if (huobiTask.Result.Success)
_prices.Add("Huobi", huobiTask.Result.Data.ClosePrice ?? 0); _prices.Add("Huobi", huobiTask.Result.Data.ClosePrice ?? 0);
+9 -19
View File
@@ -4,21 +4,12 @@
@inject IBittrexSocketClient bittrexSocketClient @inject IBittrexSocketClient bittrexSocketClient
@inject IBybitSocketClient bybitSocketClient @inject IBybitSocketClient bybitSocketClient
@inject ICoinExSocketClient coinExSocketClient @inject ICoinExSocketClient coinExSocketClient
@inject IFTXSocketClient ftxSocketClient
@inject IHuobiSocketClient huobiSocketClient @inject IHuobiSocketClient huobiSocketClient
@inject IKrakenSocketClient krakenSocketClient @inject IKrakenSocketClient krakenSocketClient
@inject IKucoinSocketClient kucoinSocketClient @inject IKucoinSocketClient kucoinSocketClient
@using Binance.Net.Clients.SpotApi @using System.Collections.Concurrent
@using Bitfinex.Net.Clients.SpotApi
@using Bittrex.Net.Clients.SpotApi
@using Bybit.Net.Clients.SpotApi
@using CoinEx.Net.Clients.SpotApi
@using CryptoExchange.Net.Objects @using CryptoExchange.Net.Objects
@using CryptoExchange.Net.Sockets @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 @implements IDisposable
<h3>ETH-BTC prices, live updates:</h3> <h3>ETH-BTC prices, live updates:</h3>
@@ -35,15 +26,14 @@
{ {
var tasks = new Task<CallResult<UpdateSubscription>>[] var tasks = new Task<CallResult<UpdateSubscription>>[]
{ {
binanceSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)), binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
bitfinexSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)), bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
bittrexSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", data.Data.LastPrice)), bittrexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", data.Data.LastPrice)),
bybitSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)), bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
coinExSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)), coinExSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)),
ftxSocketClient.Streams.SubscribeToTickerUpdatesAsync("ETH/BTC", data => UpdateData("FTX", data.Data.LastPrice ?? 0)), huobiSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)),
huobiSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)), krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)),
krakenSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)), kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
kucoinSocketClient.SpotStreams.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
}; };
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
+29 -25
View File
@@ -1,19 +1,24 @@
@page "/OrderBooks" @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.Collections.Concurrent
@using System.Timers @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 @implements IDisposable
<h3>ETH-BTC books, live updates:</h3> <h3>ETH-BTC books, live updates:</h3>
@@ -40,22 +45,21 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
// Since the Kucoin order book stream needs authentication we will need to provide API credentials beforehand // Since the Kucoin order book stream needs authentication we will need to provide API credentials beforehand
KucoinClient.SetDefaultOptions(new Kucoin.Net.Objects.KucoinClientOptions KucoinRestClient.SetDefaultOptions(options =>
{ {
ApiCredentials = new Kucoin.Net.Objects.KucoinApiCredentials("KEY", "SECRET", "PASSPHRASE") options.ApiCredentials = new Kucoin.Net.Objects.KucoinApiCredentials("KEY", "SECRET", "PASSPHRASE");
}); });
_books = new Dictionary<string, ISymbolOrderBook> _books = new Dictionary<string, ISymbolOrderBook>
{ {
{ "Binance", new BinanceSpotSymbolOrderBook("ETHBTC") }, { "Binance", binanceFactory.CreateSpot("ETHBTC") },
{ "Bitfinex", new BitfinexSymbolOrderBook("tETHBTC") }, { "Bitfinex", bitfinexFactory.Create("tETHBTC") },
{ "Bittrex", new BittrexSymbolOrderBook("ETH-BTC") }, { "Bittrex", bittrexFactory.Create("ETH-BTC") },
{ "Bybit", new BybitSpotSymbolOrderBook("ETHBTC") }, { "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) },
{ "CoinEx", new CoinExSpotSymbolOrderBook("ETHBTC") }, { "CoinEx", coinExFactory.CreateSpot("ETHBTC") },
{ "FTX", new FTXSymbolOrderBook("ETH/BTC") }, { "Huobi", huobiFactory.CreateSpot("ethbtc") },
{ "Huobi", new HuobiSpotSymbolOrderBook("ethbtc") }, { "Kraken", krakenFactory.CreateSpot("ETH/XBT") },
{ "Kraken", new KrakenSpotSymbolOrderBook("ETH/XBT") }, { "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
{ "Kucoin", new KucoinSpotSymbolOrderBook("ETH-BTC") },
}; };
await Task.WhenAll(_books.Select(b => b.Value.StartAsync())); await Task.WhenAll(_books.Select(b => b.Value.StartAsync()));
@@ -70,7 +74,7 @@
{ {
_timer.Stop(); _timer.Stop();
_timer.Dispose(); _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 // It's not necessary to wait for this
_ = book.Value.StopAsync(); _ = book.Value.StopAsync();
} }
+9 -12
View File
@@ -1,13 +1,12 @@
@page "/SpotClient" @page "/SpotClient"
@inject IBinanceClient binanceClient @inject IBinanceRestClient binanceClient
@inject IBitfinexClient bitfinexClient @inject IBitfinexRestClient bitfinexClient
@inject IBittrexClient bittrexClient @inject IBittrexRestClient bittrexClient
@inject IBybitClient bybitClient @inject IBybitRestClient bybitClient
@inject ICoinExClient coinexClient @inject ICoinExRestClient coinexClient
@inject IFTXClient ftxClient @inject IHuobiRestClient huobiClient
@inject IHuobiClient huobiClient @inject IKrakenRestClient krakenClient
@inject IKrakenClient krakenClient @inject IKucoinRestClient kucoinClient
@inject IKucoinClient kucoinClient
@using Binance.Net.Clients.SpotApi @using Binance.Net.Clients.SpotApi
@using Bitfinex.Net.Clients.SpotApi @using Bitfinex.Net.Clients.SpotApi
@using Bittrex.Net.Clients.SpotApi @using Bittrex.Net.Clients.SpotApi
@@ -15,7 +14,6 @@
@using CoinEx.Net.Clients.SpotApi @using CoinEx.Net.Clients.SpotApi
@using CryptoExchange.Net.Interfaces @using CryptoExchange.Net.Interfaces
@using CryptoExchange.Net.Interfaces.CommonClients @using CryptoExchange.Net.Interfaces.CommonClients
@using FTX.Net.Clients.TradeApi
@using Huobi.Net.Clients.SpotApi @using Huobi.Net.Clients.SpotApi
@using Kraken.Net.Clients.SpotApi @using Kraken.Net.Clients.SpotApi
@using Kucoin.Net.Clients.SpotApi @using Kucoin.Net.Clients.SpotApi
@@ -37,9 +35,8 @@
binanceClient.SpotApi.CommonSpotClient, binanceClient.SpotApi.CommonSpotClient,
bitfinexClient.SpotApi.CommonSpotClient, bitfinexClient.SpotApi.CommonSpotClient,
bittrexClient.SpotApi.CommonSpotClient, bittrexClient.SpotApi.CommonSpotClient,
bybitClient.SpotApi.CommonSpotClient, bybitClient.SpotApiV1.CommonSpotClient,
coinexClient.SpotApi.CommonSpotClient, coinexClient.SpotApi.CommonSpotClient,
ftxClient.TradeApi.CommonSpotClient,
huobiClient.SpotApi.CommonSpotClient, huobiClient.SpotApi.CommonSpotClient,
krakenClient.SpotApi.CommonSpotClient, krakenClient.SpotApi.CommonSpotClient,
kucoinClient.SpotApi.CommonSpotClient kucoinClient.SpotApi.CommonSpotClient
+4 -25
View File
@@ -2,15 +2,11 @@ using System.Collections.Generic;
using Binance.Net; using Binance.Net;
using Binance.Net.Clients; using Binance.Net.Clients;
using Binance.Net.Interfaces.Clients; using Binance.Net.Interfaces.Clients;
using Binance.Net.Objects;
using Bitfinex.Net; using Bitfinex.Net;
using Bittrex.Net; using Bittrex.Net;
using Bybit.Net; using Bybit.Net;
using CoinEx.Net; using CoinEx.Net;
using CoinEx.Net.Clients;
using CoinEx.Net.Interfaces.Clients;
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using FTX.Net;
using Huobi.Net; using Huobi.Net;
using Kraken.Net; using Kraken.Net;
using Kucoin.Net; using Kucoin.Net;
@@ -43,35 +39,18 @@ namespace BlazorClient
services.AddServerSideBlazor(); services.AddServerSideBlazor();
// Register the clients, options can be provided in the callback parameter // Register the clients, options can be provided in the callback parameter
services.AddBinance((restClientOptions, socketClientOptions) => { services.AddBinance(restOptions =>
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
{ {
ApiCredentials = new ApiCredentials("KEY", "SECRET"), restOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
LogLevel = LogLevel.Trace }, socketOptions =>
});
BinanceSocketClient.SetDefaultOptions(new BinanceSocketClientOptions
{ {
ApiCredentials = new ApiCredentials("KEY", "SECRET"), socketOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
}); });
services.AddTransient<IBinanceClient, BinanceClient>();
services.AddScoped<IBinanceSocketClient, BinanceSocketClient>();
services.AddBitfinex(); services.AddBitfinex();
services.AddBittrex(); services.AddBittrex();
services.AddBybit(); services.AddBybit();
services.AddCoinEx(); services.AddCoinEx();
services.AddFTX();
services.AddHuobi(); services.AddHuobi();
services.AddKraken(); services.AddKraken();
services.AddKucoin(); services.AddKucoin();
-1
View File
@@ -13,7 +13,6 @@
@using Bittrex.Net.Interfaces.Clients; @using Bittrex.Net.Interfaces.Clients;
@using Bybit.Net.Interfaces.Clients; @using Bybit.Net.Interfaces.Clients;
@using CoinEx.Net.Interfaces.Clients; @using CoinEx.Net.Interfaces.Clients;
@using FTX.Net.Interfaces.Clients;
@using Huobi.Net.Interfaces.Clients; @using Huobi.Net.Interfaces.Clients;
@using Kraken.Net.Interfaces.Clients; @using Kraken.Net.Interfaces.Clients;
@using Kucoin.Net.Interfaces.Clients; @using Kucoin.Net.Interfaces.Clients;
+8 -9
View File
@@ -6,15 +6,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Binance.Net" Version="8.0.6" /> <PackageReference Include="Binance.Net" Version="9.0.1" />
<PackageReference Include="Bitfinex.Net" Version="5.0.3" /> <PackageReference Include="Bitfinex.Net" Version="6.0.0" />
<PackageReference Include="Bittrex.Net" Version="7.0.4" /> <PackageReference Include="Bittrex.Net" Version="8.0.0" />
<PackageReference Include="Bybit.Net" Version="0.0.4" /> <PackageReference Include="Bybit.Net" Version="3.0.0" />
<PackageReference Include="CoinEx.Net" Version="5.0.3" /> <PackageReference Include="CoinEx.Net" Version="6.0.0" />
<PackageReference Include="FTX.Net" Version="1.0.4" /> <PackageReference Include="Huobi.Net" Version="5.0.0" />
<PackageReference Include="Huobi.Net" Version="4.0.4" /> <PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
<PackageReference Include="KrakenExchange.Net" Version="3.0.3" /> <PackageReference Include="Kucoin.Net" Version="5.0.0" />
<PackageReference Include="Kucoin.Net" Version="4.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -17,21 +17,21 @@ namespace ConsoleClient.Exchanges
public async Task<WebCallResult> CancelOrder(string symbol, string id) 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)); var result = await client.SpotApi.Trading.CancelOrderAsync(symbol, long.Parse(id));
return result.AsDataless(); return result.AsDataless();
} }
public async Task<Dictionary<string, decimal>> GetBalances() public async Task<Dictionary<string, decimal>> GetBalances()
{ {
using var client = new BinanceClient(); using var client = new BinanceRestClient();
var result = await client.SpotApi.Account.GetAccountInfoAsync(); var result = await client.SpotApi.Account.GetAccountInfoAsync();
return result.Data.Balances.ToDictionary(b => b.Asset, b => b.Total); return result.Data.Balances.ToDictionary(b => b.Asset, b => b.Total);
} }
public async Task<IEnumerable<OpenOrder>> GetOpenOrders() public async Task<IEnumerable<OpenOrder>> GetOpenOrders()
{ {
using var client = new BinanceClient(); using var client = new BinanceRestClient();
var result = await client.SpotApi.Trading.GetOpenOrdersAsync(); var result = await client.SpotApi.Trading.GetOpenOrdersAsync();
// Should check result success status here // Should check result success status here
return result.Data.Select(o => new OpenOrder return result.Data.Select(o => new OpenOrder
@@ -49,7 +49,7 @@ namespace ConsoleClient.Exchanges
public async Task<decimal> GetPrice(string symbol) 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); var result = await client.SpotApi.ExchangeData.GetPriceAsync(symbol);
// Should check result success status here // Should check result success status here
return result.Data.Price; 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) 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( var result = await client.SpotApi.Trading.PlaceOrderAsync(
symbol, symbol,
side.ToLower() == "buy" ? Binance.Net.Enums.OrderSide.Buy: Binance.Net.Enums.OrderSide.Sell, 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) 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; 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 System.Threading.Tasks;
using Binance.Net.Clients; using Binance.Net.Clients;
using Binance.Net.Objects; using Binance.Net.Objects;
using Bybit.Net.Clients;
using ConsoleClient.Exchanges; using ConsoleClient.Exchanges;
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Sockets; using CryptoExchange.Net.Sockets;
using FTX.Net.Clients;
using FTX.Net.Objects;
using Microsoft.Extensions.Logging;
namespace ConsoleClient namespace ConsoleClient
{ {
@@ -19,20 +17,18 @@ namespace ConsoleClient
static Dictionary<string, IExchange> _exchanges = new Dictionary<string, IExchange> static Dictionary<string, IExchange> _exchanges = new Dictionary<string, IExchange>
{ {
{ "Binance", new BinanceExchange() }, { "Binance", new BinanceExchange() },
{ "FTX", new FTXExchange() } { "Bybit", new BybitExchange() }
}; };
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
BinanceClient.SetDefaultOptions(new BinanceClientOptions BinanceRestClient.SetDefaultOptions(options =>
{ {
LogLevel = LogLevel.Trace, options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
ApiCredentials = new ApiCredentials("APIKEY", "APISECRET")
}); });
FTXClient.SetDefaultOptions(new FTXClientOptions BybitRestClient.SetDefaultOptions(options =>
{ {
LogLevel = LogLevel.Trace, options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
ApiCredentials = new ApiCredentials("APIKEY", "APISECRET")
}); });
while (true) while (true)
+6
View File
@@ -33,6 +33,12 @@ 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). Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
## Release notes ## Release notes
* Version 6.0.2 - 05 Jul 2023
* Added properties generic dictionary to SocketConnection
* Version 6.0.1 - 29 Jun 2023
* Added LogLevel optional parameter to TraceLoggerProvider
* Version 6.0.0 - 25 Jun 2023 * Version 6.0.0 - 25 Jun 2023
* Updated ApiCredentials to support RSA signing as well as the default Hmac signature * 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 * Removed custom logging implementation in favor of using `Microsoft.Extensions.Logging` ILogger directly