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

Compare commits

..

10 Commits

Author SHA1 Message Date
JKorf 67299338a8 Updated version 2024-04-28 10:59:59 +02:00
Jan Korf 971c049c5f Feature/cryptoclients update (#198) 2024-04-28 10:56:51 +02:00
JKorf bb7ba5ea49 Updated version 2024-04-23 19:03:34 +02:00
JKorf 747c986644 Update DateTimeConverter.cs 2024-04-23 16:44:42 +02:00
JKorf d88087c8ac Fix concurrency issue request definition cache 2024-04-23 14:56:10 +02:00
JKorf 968bdc330e Added new datetime format support 2024-04-23 14:55:57 +02:00
JKorf 7b49562c1d Fixed reconnect url logging even when url hasn't changed 2024-04-23 09:11:05 +02:00
JKorf 24ba60da47 Added error message context 2024-04-21 11:48:11 +02:00
JKorf ed5a07fbdb Updated version 2024-04-19 11:47:21 +02:00
JKorf 3d3a9b88e7 Fix for endpoint specific rate limiting throwing exception 2024-04-19 11:45:46 +02:00
16 changed files with 134 additions and 15 deletions
@@ -54,6 +54,8 @@ namespace CryptoExchange.Net.UnitTests
return deserializeResult;
}
/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
public override TimeSpan? GetTimeOffset() => null;
public override TimeSyncInfo GetTimeSyncInfo() => null;
protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials) => throw new NotImplementedException();
@@ -137,6 +137,9 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
RequestFactory = new Mock<IRequestFactory>().Object;
}
/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
public async Task<CallResult<T>> Request<T>(CancellationToken ct = default) where T : class
{
return await SendRequestAsync<T>(new Uri("http://www.test.com"), HttpMethod.Get, ct, requestWeight: 0);
@@ -178,6 +181,9 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
RequestFactory = new Mock<IRequestFactory>().Object;
}
/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
public async Task<CallResult<T>> Request<T>(CancellationToken ct = default) where T : class
{
return await SendRequestAsync<T>(new Uri("http://www.test.com"), HttpMethod.Get, ct, requestWeight: 0);
@@ -84,6 +84,9 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
}
/// <inheritdoc />
public override string FormatSymbol(string baseAsset, string quoteAsset) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
internal IWebsocket CreateSocketInternal(string address)
{
return CreateSocket(address);
@@ -78,6 +78,9 @@ namespace CryptoExchange.Net.Clients
/// <returns></returns>
protected abstract AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials);
/// <inheritdoc />
public abstract string FormatSymbol(string baseAsset, string quoteAsset);
/// <inheritdoc />
public void SetApiCredentials<T>(T credentials) where T : ApiCredentials
{
+3 -1
View File
@@ -585,7 +585,9 @@ namespace CryptoExchange.Net.Clients
error = rateError;
}
else
{
error = ParseErrorResponse((int)response.StatusCode, response.ResponseHeaders, accessor);
}
if (error.Code == null || error.Code == 0)
error.Code = (int)response.StatusCode;
@@ -601,7 +603,7 @@ namespace CryptoExchange.Net.Clients
if (!valid)
{
// Invalid json
var error = new ServerError(accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]");
var error = new ServerError("Failed to parse response", accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]");
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
}
@@ -68,6 +68,21 @@ namespace CryptoExchange.Net.Converters.JsonNet
return objectType == typeof(DateTime) ? default(DateTime) : null;
}
if (stringValue.Length == 12 && stringValue.StartsWith("202"))
{
// Parse 202303261200 format
if (!int.TryParse(stringValue.Substring(0, 4), out var year)
|| !int.TryParse(stringValue.Substring(4, 2), out var month)
|| !int.TryParse(stringValue.Substring(6, 2), out var day)
|| !int.TryParse(stringValue.Substring(8, 2), out var hour)
|| !int.TryParse(stringValue.Substring(10, 2), out var minute))
{
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + reader.Value);
return default;
}
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
}
if (stringValue.Length == 8)
{
// Parse 20211103 format
@@ -68,7 +68,22 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return default;
}
if (stringValue!.Length == 8)
if (stringValue!.Length == 12 && stringValue.StartsWith("202"))
{
// Parse 202303261200 format
if (!int.TryParse(stringValue.Substring(0, 4), out var year)
|| !int.TryParse(stringValue.Substring(4, 2), out var month)
|| !int.TryParse(stringValue.Substring(6, 2), out var day)
|| !int.TryParse(stringValue.Substring(8, 2), out var hour)
|| !int.TryParse(stringValue.Substring(10, 2), out var minute))
{
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
return default;
}
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
}
if (stringValue.Length == 8)
{
// Parse 20211103 format
if (!int.TryParse(stringValue.Substring(0, 4), out var year)
+3 -3
View File
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors>
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
<PackageVersion>7.3.1</PackageVersion>
<AssemblyVersion>7.3.1</AssemblyVersion>
<FileVersion>7.3.1</FileVersion>
<PackageVersion>7.4.0</PackageVersion>
<AssemblyVersion>7.4.0</AssemblyVersion>
<FileVersion>7.4.0</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange</PackageTags>
<RepositoryType>git</RepositoryType>
@@ -12,6 +12,14 @@ namespace CryptoExchange.Net.Interfaces
/// </summary>
string BaseAddress { get; }
/// <summary>
/// Format a base and quote asset to an exchange accepted symbol
/// </summary>
/// <param name="baseAsset">The base asset</param>
/// <param name="quoteAsset">The quote asset</param>
/// <returns></returns>
string FormatSymbol(string baseAsset, string quoteAsset);
/// <summary>
/// Set the API credentials for this API client
/// </summary>
@@ -0,0 +1,27 @@
using CryptoExchange.Net.Objects.Options;
using System;
namespace CryptoExchange.Net.Interfaces
{
/// <summary>
/// Factory for ISymbolOrderBook instances
/// </summary>
public interface IOrderBookFactory<TOptions> where TOptions : OrderBookOptions
{
/// <summary>
/// Create a new order book by symbol name
/// </summary>
/// <param name="symbol">Symbol name</param>
/// <param name="options">Options for the order book</param>
/// <returns></returns>
public ISymbolOrderBook Create(string symbol, Action<TOptions>? options = null);
/// <summary>
/// Create a new order book by base and quote asset names
/// </summary>
/// <param name="baseAsset">Base asset name</param>
/// <param name="quoteAsset">Quote asset name</param>
/// <param name="options">Options for the order book</param>
/// <returns></returns>
public ISymbolOrderBook Create(string baseAsset, string quoteAsset, Action<TOptions>? options = null);
}
}
@@ -3,7 +3,7 @@
/// <summary>
/// Base for order book options
/// </summary>
public class OrderBookOptions : ExchangeOptions
public class OrderBookOptions
{
/// <summary>
/// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
@@ -19,11 +19,7 @@
{
return new T
{
ApiCredentials = ApiCredentials?.Copy(),
OutputOriginalData = OutputOriginalData,
ChecksumValidationEnabled = ChecksumValidationEnabled,
Proxy = Proxy,
RequestTimeout = RequestTimeout
};
}
}
@@ -1,5 +1,6 @@
using CryptoExchange.Net.RateLimiting.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Http;
@@ -10,7 +11,7 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public class RequestDefinitionCache
{
private readonly Dictionary<string, RequestDefinition> _definitions = new();
private readonly ConcurrentDictionary<string, RequestDefinition> _definitions = new();
/// <summary>
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
@@ -74,7 +75,7 @@ namespace CryptoExchange.Net.Objects
RequestBodyFormat = requestBodyFormat,
ParameterPosition = parameterPosition,
};
_definitions.Add(method + path, def);
_definitions.TryAdd(method + path, def);
}
return def;
@@ -0,0 +1,30 @@
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects.Options;
using System;
namespace CryptoExchange.Net.OrderBook
{
/// <inheritdoc />
public class OrderBookFactory<TOptions> : IOrderBookFactory<TOptions> where TOptions: OrderBookOptions
{
private readonly Func<string, Action<TOptions>?, ISymbolOrderBook> _symbolCtor;
private readonly Func<string, string, Action<TOptions>?, ISymbolOrderBook> _assetsCtor;
/// <summary>
/// ctor
/// </summary>
/// <param name="symbolCtor"></param>
/// <param name="assetsCtor"></param>
public OrderBookFactory(Func<string, Action<TOptions>?, ISymbolOrderBook> symbolCtor, Func<string, string, Action<TOptions>?, ISymbolOrderBook> assetsCtor)
{
_symbolCtor = symbolCtor;
_assetsCtor = assetsCtor;
}
/// <inheritdoc />
public ISymbolOrderBook Create(string symbol, Action<TOptions>? options = null) => _symbolCtor(symbol, options);
/// <inheritdoc />
public ISymbolOrderBook Create(string baseAsset, string quoteAsset, Action<TOptions>? options = null) => _assetsCtor(baseAsset, quoteAsset, options);
}
}
@@ -52,7 +52,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
/// <inheritdoc />
public RateLimitState ApplyWeight(RateLimitItemType type, RequestDefinition definition, string host, SecureString? apiKey, int requestWeight)
{
var key = definition.Path + definition.Method + definition;
var key = definition.Path + definition.Method;
var tracker = _trackers[key];
tracker.ApplyWeight(requestWeight);
return RateLimitState.Applied(definition.EndpointLimitCount!.Value, definition.EndpointLimitPeriod!.Value, tracker.Current);
@@ -270,7 +270,7 @@ namespace CryptoExchange.Net.Sockets
if (task != null)
{
var reconnectUri = await task.ConfigureAwait(false);
if (reconnectUri != null && Parameters.Uri != reconnectUri)
if (reconnectUri != null && Parameters.Uri.ToString() != reconnectUri.ToString())
{
_logger.SocketSetReconnectUri(Id, reconnectUri);
Parameters.Uri = reconnectUri;
@@ -729,7 +729,7 @@ namespace CryptoExchange.Net.Sockets
public int Id { get; set; }
/// <summary>
/// The request id
/// The request weight
/// </summary>
public int Weight { get; set; }
+11
View File
@@ -42,6 +42,17 @@ 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 7.4.0 - 28 Apr 2024
* Added FormatSymbol on IBaseApiClient interface, Added IOrderBookFactory interface, Removed ExchangeOptions as base class for OrderBookOptions
* Version 7.3.3 - 23 Apr 2024
* Added support for new DateTime format parsing
* Updated some logging
* Fixed concurrency issue in rest request sending
* Version 7.3.2 - 19 Apr 2024
* Fix for endpoint specific rate limiting throwing exception
* Version 7.3.1 - 18 Apr 2024
* Fixed websocket system subscriptions getting marked as unconfirmed when reconnecting