1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-07 07:56:12 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Jkorf
9450d447b9 Updated version 2022-02-18 11:05:46 +01:00
Jkorf
bc0b55f337 Added clientOrderId parameter to common clients 2022-02-18 10:09:26 +01:00
Jkorf
31111006c7 Update SpotClient.razor 2022-02-17 16:32:53 +01:00
Jkorf
e7400ce334 Made some names more generic 2022-02-17 16:10:49 +01:00
Jkorf
9bdef400da Updated vesrion 2022-02-15 11:38:41 +01:00
Jkorf
3b80a945ee docs 2022-02-15 11:34:50 +01:00
Jkorf
0268e211e9 Immediate initial reconnect attempt when connection is lost 2022-02-15 09:56:45 +01:00
Jkorf
6eb43c5218 Re-added recalculation interval 2022-02-11 13:59:05 +01:00
15 changed files with 144 additions and 131 deletions

View File

@ -19,9 +19,9 @@ namespace CryptoExchange.Net
public abstract class BaseClient : IDisposable
{
/// <summary>
/// The name of the exchange the client is for
/// The name of the API the client is for
/// </summary>
internal string ExchangeName { get; }
internal string Name { get; }
/// <summary>
/// Api clients in this client
/// </summary>
@ -56,19 +56,19 @@ namespace CryptoExchange.Net
/// <summary>
/// ctor
/// </summary>
/// <param name="exchangeName">The name of the exchange this client is for</param>
/// <param name="name">The name of the API this client is for</param>
/// <param name="options">The options for this client</param>
protected BaseClient(string exchangeName, BaseClientOptions options)
protected BaseClient(string name, BaseClientOptions options)
{
log = new Log(exchangeName);
log = new Log(name);
log.UpdateWriters(options.LogWriters);
log.Level = options.LogLevel;
ClientOptions = options;
ExchangeName = exchangeName;
Name = name;
log.Write(LogLevel.Trace, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
log.Write(LogLevel.Trace, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {name}.Net: v{GetType().Assembly.GetName().Version}");
}
/// <summary>
@ -278,7 +278,7 @@ namespace CryptoExchange.Net
/// </summary>
public virtual void Dispose()
{
log.Write(LogLevel.Debug, "Disposing exchange client");
log.Write(LogLevel.Debug, "Disposing client");
foreach (var client in ApiClients)
client.Dispose();
}

View File

@ -74,15 +74,15 @@ namespace CryptoExchange.Net
/// <summary>
/// ctor
/// </summary>
/// <param name="exchangeName">The name of the exchange this client is for</param>
/// <param name="exchangeOptions">The options for this client</param>
protected BaseRestClient(string exchangeName, BaseRestClientOptions exchangeOptions) : base(exchangeName, exchangeOptions)
/// <param name="name">The name of the API this client is for</param>
/// <param name="options">The options for this client</param>
protected BaseRestClient(string name, BaseRestClientOptions options) : base(name, options)
{
if (exchangeOptions == null)
throw new ArgumentNullException(nameof(exchangeOptions));
if (options == null)
throw new ArgumentNullException(nameof(options));
ClientOptions = exchangeOptions;
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
ClientOptions = options;
RequestFactory.Configure(options.RequestTimeout, options.Proxy, options.HttpClient);
}
/// <inheritdoc />

View File

@ -101,14 +101,14 @@ namespace CryptoExchange.Net
/// <summary>
/// ctor
/// </summary>
/// <param name="exchangeName">The name of the exchange this client is for</param>
/// <param name="exchangeOptions">The options for this client</param>
protected BaseSocketClient(string exchangeName, BaseSocketClientOptions exchangeOptions): base(exchangeName, exchangeOptions)
/// <param name="name">The name of the API this client is for</param>
/// <param name="options">The options for this client</param>
protected BaseSocketClient(string name, BaseSocketClientOptions options) : base(name, options)
{
if (exchangeOptions == null)
throw new ArgumentNullException(nameof(exchangeOptions));
if (options == null)
throw new ArgumentNullException(nameof(options));
ClientOptions = exchangeOptions;
ClientOptions = options;
}
/// <summary>

View File

@ -65,7 +65,7 @@ namespace CryptoExchange.Net
var timeSyncParams = GetTimeSyncInfo();
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
{
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < TimeSpan.FromHours(1)))
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval))
{
timeSyncParams.TimeSyncState.Semaphore.Release();
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, true, null);

View File

@ -5,17 +5,17 @@
<PropertyGroup>
<PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors>
<Description>A base package for implementing cryptocurrency exchange API's</Description>
<PackageVersion>5.0.0-beta8</PackageVersion>
<Description>A base package for implementing cryptocurrency API's</Description>
<PackageVersion>5.0.0</PackageVersion>
<AssemblyVersion>5.0.0</AssemblyVersion>
<FileVersion>5.0.0-beta8</FileVersion>
<FileVersion>5.0.0</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>5.0.0-beta8 - Fix for time offset calculation not updating the time offset when time offset is smaller than 500ms</PackageReleaseNotes>
<PackageReleaseNotes>See https://github.com/JKorf/CryptoExchange.Net#release-notes</PackageReleaseNotes>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces
namespace CryptoExchange.Net.Interfaces.CommonClients
{
/// <summary>
/// Common rest client endpoints
@ -135,50 +135,4 @@ namespace CryptoExchange.Net.Interfaces
/// <returns></returns>
Task<WebCallResult<OrderId>> CancelOrderAsync(string orderId, string? symbol = null, CancellationToken ct = default);
}
/// <summary>
/// Common futures endpoints
/// </summary>
public interface IFuturesClient: IBaseRestClient
{
/// <summary>
/// Place an order
/// </summary>
/// <param name="symbol">The symbol the order is for</param>
/// <param name="side">The side of the order</param>
/// <param name="type">The type of the order</param>
/// <param name="quantity">The quantity of the order</param>
/// <param name="price">The price of the order, only for limit orders</param>
/// <param name="accountId">[Optional] The account id to place the order on, required for some exchanges, ignored otherwise</param>
/// <param name="leverage">[Optional] Leverage for this order. This is needed for some exchanges. For exchanges where this is not needed this parameter is ignored (and should be set before hand)</param>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns>The id of the resulting order</returns>
Task<WebCallResult<OrderId>> PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price = null, int? leverage = null, string? accountId = null, CancellationToken ct = default);
/// <summary>
/// Get position
/// </summary>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns></returns>
Task<WebCallResult<IEnumerable<Position>>> GetPositionsAsync(CancellationToken ct = default);
}
/// <summary>
/// Common spot endpoints
/// </summary>
public interface ISpotClient: IBaseRestClient
{
/// <summary>
/// Place an order
/// </summary>
/// <param name="symbol">The symbol the order is for</param>
/// <param name="side">The side of the order</param>
/// <param name="type">The type of the order</param>
/// <param name="quantity">The quantity of the order</param>
/// <param name="price">The price of the order, only for limit orders</param>
/// <param name="accountId">[Optional] The account id to place the order on, required for some exchanges, ignored otherwise</param>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns>The id of the resulting order</returns>
Task<WebCallResult<OrderId>> PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price = null, string? accountId = null, CancellationToken ct = default);
}
}

View File

@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CryptoExchange.Net.CommonObjects;
using CryptoExchange.Net.Interfaces.CommonClients;
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.Interfaces.CommonClients
{
/// <summary>
/// Common futures endpoints
/// </summary>
public interface IFuturesClient : IBaseRestClient
{
/// <summary>
/// Place an order
/// </summary>
/// <param name="symbol">The symbol the order is for</param>
/// <param name="side">The side of the order</param>
/// <param name="type">The type of the order</param>
/// <param name="quantity">The quantity of the order</param>
/// <param name="price">The price of the order, only for limit orders</param>
/// <param name="accountId">[Optional] The account id to place the order on, required for some exchanges, ignored otherwise</param>
/// <param name="leverage">[Optional] Leverage for this order. This is needed for some exchanges. For exchanges where this is not needed this parameter is ignored (and should be set before hand)</param>
/// <param name="clientOrderId">[Optional] Client specified id for this order</param>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns>The id of the resulting order</returns>
Task<WebCallResult<OrderId>> PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price = null, int? leverage = null, string? accountId = null, string? clientOrderId = null, CancellationToken ct = default);
/// <summary>
/// Get position
/// </summary>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns></returns>
Task<WebCallResult<IEnumerable<Position>>> GetPositionsAsync(CancellationToken ct = default);
}
}

View File

@ -0,0 +1,28 @@
using CryptoExchange.Net.CommonObjects;
using CryptoExchange.Net.Interfaces.CommonClients;
using CryptoExchange.Net.Objects;
using System.Threading;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces.CommonClients
{
/// <summary>
/// Common spot endpoints
/// </summary>
public interface ISpotClient: IBaseRestClient
{
/// <summary>
/// Place an order
/// </summary>
/// <param name="symbol">The symbol the order is for</param>
/// <param name="side">The side of the order</param>
/// <param name="type">The type of the order</param>
/// <param name="quantity">The quantity of the order</param>
/// <param name="price">The price of the order, only for limit orders</param>
/// <param name="accountId">[Optional] The account id to place the order on, required for some exchanges, ignored otherwise</param>
/// <param name="clientOrderId">[Optional] Client specified id for this order</param>
/// <param name="ct">[Optional] Cancellation token for cancelling the request</param>
/// <returns>The id of the resulting order</returns>
Task<WebCallResult<OrderId>> PlaceOrderAsync(string symbol, CommonOrderSide side, CommonOrderType type, decimal quantity, decimal? price = null, string? accountId = null, string? clientOrderId = null, CancellationToken ct = default);
}
}

View File

@ -163,7 +163,7 @@ namespace CryptoExchange.Net.Objects
public TimeSpan SocketNoDataTimeout { get; set; }
/// <summary>
/// The amount of subscriptions that should be made on a single socket connection. Not all exchanges support multiple subscriptions on a single socket.
/// 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>
@ -280,6 +280,11 @@ namespace CryptoExchange.Net.Objects
/// </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>
@ -318,12 +323,13 @@ namespace CryptoExchange.Net.Objects
input.RateLimiters = def.RateLimiters.ToList();
input.RateLimitingBehaviour = def.RateLimitingBehaviour;
input.AutoTimestamp = def.AutoTimestamp;
input.TimestampRecalculationInterval = def.TimestampRecalculationInterval;
}
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}, RateLimiters: {RateLimiters?.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, AutoTimestamp: {AutoTimestamp}";
return $"{base.ToString()}, RateLimiters: {RateLimiters?.Count}, RateLimitBehaviour: {RateLimitingBehaviour}, AutoTimestamp: {AutoTimestamp}, TimestampRecalculationInterval: {TimestampRecalculationInterval}";
}
}

View File

@ -46,6 +46,10 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public bool SyncTime { get; }
/// <summary>
/// Timestamp recalulcation interval
/// </summary>
public TimeSpan RecalculationInterval { get; }
/// <summary>
/// Time sync state for the API client
/// </summary>
public TimeSyncState TimeSyncState { get; }

View File

@ -377,7 +377,7 @@ namespace CryptoExchange.Net.Sockets
Socket.Reconnecting = true;
DisconnectTime = DateTime.UtcNow;
log.Write(LogLevel.Information, $"Socket {Socket.Id} Connection lost, will try to reconnect after {socketClient.ClientOptions.ReconnectInterval}");
log.Write(LogLevel.Information, $"Socket {Socket.Id} Connection lost, will try to reconnect{(ReconnectTry == 0 ? "": $" after {socketClient.ClientOptions.ReconnectInterval}")}");
if (!lostTriggered)
{
lostTriggered = true;
@ -388,8 +388,12 @@ namespace CryptoExchange.Net.Sockets
{
while (ShouldReconnect)
{
// Wait a bit before attempting reconnect
await Task.Delay(socketClient.ClientOptions.ReconnectInterval).ConfigureAwait(false);
if (ReconnectTry > 0)
{
// Wait a bit before attempting reconnect
await Task.Delay(socketClient.ClientOptions.ReconnectInterval).ConfigureAwait(false);
}
if (!ShouldReconnect)
{
// Should reconnect changed to false while waiting to reconnect
@ -416,7 +420,7 @@ namespace CryptoExchange.Net.Sockets
break;
}
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect{(socketClient.ClientOptions.MaxReconnectTries != null ? $", try {ReconnectTry}/{socketClient.ClientOptions.MaxReconnectTries}": "")}");
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect{(socketClient.ClientOptions.MaxReconnectTries != null ? $", try {ReconnectTry}/{socketClient.ClientOptions.MaxReconnectTries}": "")}, will try again in {socketClient.ClientOptions.ReconnectInterval}");
continue;
}

View File

@ -49,7 +49,6 @@
foreach(var task in tasks)
{
if(task.Item2.Result.Success)
// TODO new version
_prices.Add(task.Item1, task.Item2.Result.Data.HighPrice);
}
}

View File

@ -18,56 +18,21 @@ I develop and maintain this package on my own for free in my spare time. Donatio
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf)
## Release notes
* Version 5.0.0-beta8 - 09 Feb 2022
* Fix for time offset calculation not updating the time offset when time offset is smaller than 500ms
* Version 5.0.0-beta7 - 05 Feb 2022
* Added CancellationToken support on Common client interfaces
* Added CancellationToken support on SymbolOrderBook
* Improved SymbolOrderBook Start/Stop robustness
* Version 5.0.0-beta6 - 02 Feb 2022
* Fix for deserialization of DateTime string "0" to a non-nullable DateTime property in .net framework
* Version 5.0.0-beta5 - 25 Jan 2022
* Fixed DateTime string including nanoseconds deserialization in DateTimeConverter
* Refactored SymbolOrderBook to use AsyncResetEvent instead of AutoResetEvent to prevent thread blocking
* Version 5.0.0-beta4 - 24 Jan 2022
* Fixed dependencies
* Version 5.0.0-beta3 - 24 Jan 2022
* Re-added Common- prefixes for Common client enums to avoid Ambiguous references
* Version 5.0.0-beta2 - 21 Jan 2022
* Replaced Debug.WriteLine with Trace.WriteLine
* Version 5.0.0-beta1 - 15 Jan 2022
* Added additional properties to WebCallResult
* Added CallResult unit tests
* Updated some logging for client options
* Version 5.0.0-alpha7 - 07 Jan 2022
* Fixed SymbolOrderBook ToString() not being threadsafe, Potential fix request timeout unclear error message
* Version 5.0.0-alpha6 - 03 Jan 2022
* Fixed typo Comon -> Common
* Version 5.0.0-alpha5 - 01 Jan 2022
* Reverted conditional dependencies, not working as expected
* Version 5.0.0-alpha4 - 01 Jan 2022
* New comon interface structure
* Added Microsoft.Extensions.DependencyInjection.Abstractions to allow a AddXXX extension method on IServiceCollection for implementations
* Version 5.0.0-alpha3 - 27 Dec 2021
* Added ExchangeName to IExchangeClient interface
* Version 5.0.0-alpha2 - 21 Dec 2021
* Rework authentication provider, support for time syncing, various internal updates
* Version 5.0.0-alpha1 - 07 Dec 2021
* New client structures, multiple improvements and changes. Details will be available later
* Version 5.0.0
* Added Github.io page for documentation: https://jkorf.github.io/CryptoExchange.Net/
* Added single DateTimeConverter replacing the different timestamp converters
* Added additional request related properties to WebCallResult
* Added CancelationToken support for websockets
* Added CancelationToken support for SymbolOrderBook starting
* Added TimeSync support
* Refactored base client classes into BaseClient and ApiClient to provide a more defined client structure
* Refactored client options to have better control over each different ApiClient
* Refactored authentication provider to be more flexible
* Refactored rate limiter implementation
* Refactored IExchangeClient interface to ISpotClient and IFuturesClient
* Refactored socket reconnection to immediately try to reconnect before waiting the ReconnectTimeout
* Improved SymbolOrderBook stability
* Updated code docs
* Version 4.2.8 - 08 Oct 2021
* Fixed deadlock in socket receive

View File

@ -45,4 +45,20 @@ private void SomeMethod()
});
}
```
### Can I use the TestNet/US/other API with this library
Yes, generally these are all supported and can be configured by setting the BaseAddress in the client options. Some known API addresses should be available in the [Exchange]ApiAddresses class. For example:
```csharp
var client = new BinanceClient(new BinanceClientOptions
{
SpotApiOptions = new BinanceApiClientOptions
{
BaseAddress = BinanceApiAddresses.TestNet.RestClientAddress
},
UsdFuturesApiOptions = new BinanceApiClientOptions
{
BaseAddress = BinanceApiAddresses.TestNet.UsdFuturesRestClientAddress
}
});
```

View File

@ -109,7 +109,7 @@ All clients have access to the following options, specific implementations might
|Option|Description|Default|
|------|-----------|-------|
|`ApiCredentials`|The API credentials to use for this specific API client. Will override any credentials provided in the base client options|
|`BaseAddress`|The base address to the API. All calls to the API will use this base address as basis for the endpoints. This allows for swapping to test API's or swapping to a different cluster for example.|Depends on implementation
|`BaseAddress`|The base address to the API. All calls to the API will use this base address as basis for the endpoints. This allows for swapping to test API's or swapping to a different cluster for example. Available base addresses are defined in the [Library]ApiAddresses helper class, for example `KucoinApiAddresses`|Depends on implementation
**Options for Rest Api Client (extension of base api client options)**