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

Compare commits

...

6 Commits

9 changed files with 171 additions and 9 deletions
@@ -802,7 +802,6 @@ namespace CryptoExchange.Net.Clients
return new CallResult<HighPerfSocketConnection<TUpdateType>>(socketConnection); return new CallResult<HighPerfSocketConnection<TUpdateType>>(socketConnection);
} }
/// <summary> /// <summary>
/// Process an unhandled message /// Process an unhandled message
/// </summary> /// </summary>
@@ -811,6 +810,14 @@ namespace CryptoExchange.Net.Clients
{ {
} }
/// <summary>
/// Process an unhandled message
/// </summary>
/// <param name="connection">The socket connection</param>
/// <param name="typeIdentifier">The type as identified</param>
/// <param name="data">The data</param>
protected internal virtual bool HandleUnhandledMessage(SocketConnection connection, string typeIdentifier, ReadOnlySpan<byte> data) => false;
/// <summary> /// <summary>
/// Process connect rate limited /// Process connect rate limited
/// </summary> /// </summary>
@@ -0,0 +1,30 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson
{
/// <summary>
/// Converter for comma separated string values
/// </summary>
public class CommaSplitStringConverter : JsonConverter<string[]>
{
/// <inheritdoc />
public override string[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var str = reader.GetString();
if (string.IsNullOrEmpty(str))
return [];
return str!.Split(',').ToArray() ?? [];
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, string[] value, JsonSerializerOptions options)
{
writer.WriteStringValue(string.Join(",", value));
}
}
}
+3 -3
View File
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <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> <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>10.2.2</PackageVersion> <PackageVersion>10.2.4</PackageVersion>
<AssemblyVersion>10.2.2</AssemblyVersion> <AssemblyVersion>10.2.4</AssemblyVersion>
<FileVersion>10.2.2</FileVersion> <FileVersion>10.2.4</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <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;CryptoExchange.Net</PackageTags> <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;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
+26 -1
View File
@@ -74,8 +74,13 @@ namespace CryptoExchange.Net
{ {
if (serializationType == ArrayParametersSerialization.Array) if (serializationType == ArrayParametersSerialization.Array)
{ {
foreach(var entry in (object[])parameter.Value) bool firstArrayValue = true;
foreach (var entry in (object[])parameter.Value)
{ {
if (!firstArrayValue)
uriString.Append('&');
firstArrayValue = false;
uriString.Append(parameter.Key); uriString.Append(parameter.Key);
uriString.Append("[]="); uriString.Append("[]=");
if (urlEncodeValues) if (urlEncodeValues)
@@ -611,6 +616,26 @@ namespace CryptoExchange.Net
return services; return services;
} }
/// <summary>
/// Convert a hex encoded string to byte array
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] HexStringToBytes(this string hexString)
{
if (hexString.StartsWith("0x"))
hexString = hexString.Substring(2);
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
string hexSubstring = hexString.Substring(i, 2);
bytes[i / 2] = Convert.ToByte(hexSubstring, 16);
}
return bytes;
}
} }
} }
@@ -96,6 +96,23 @@ namespace CryptoExchange.Net.Objects
base.Add(key, value.Value.ToString(CultureInfo.InvariantCulture)); base.Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
} }
/// <summary>
/// Add a DateTime value as string
/// </summary>
public void AddString(string key, DateTime value)
{
base.Add(key, value.ToString("yyyy-MM-ddTHH:mm:ssZ"));
}
/// <summary>
/// Add a DateTime value as string. Not added if value is null
/// </summary>
public void AddOptionalString(string key, DateTime? value)
{
if (value != null)
base.Add(key, value.Value.ToString("yyyy-MM-ddTHH:mm:ssZ"));
}
/// <summary> /// <summary>
/// Add a datetime value as milliseconds timestamp /// Add a datetime value as milliseconds timestamp
/// </summary> /// </summary>
@@ -241,6 +258,45 @@ namespace CryptoExchange.Net.Objects
base.Add(key, int.Parse(stringVal)); base.Add(key, int.Parse(stringVal));
} }
} }
/// <summary>
/// Add key as comma separated values
/// </summary>
public void AddCommaSeparated(string key, IEnumerable<string> values)
{
base.Add(key, string.Join(",", values));
}
/// <summary>
/// Add key as comma separated values if there are values provided
/// </summary>
public void AddOptionalCommaSeparated(string key, IEnumerable<string>? values)
{
if (values == null || !values.Any())
return;
base.Add(key, string.Join(",", values));
}
/// <summary>
/// Add key as boolean lower case value
/// </summary>
public void AddBoolString(string key, bool value)
{
base.Add(key, value.ToString().ToLower());
}
/// <summary>
/// Add key as boolean lower case value if it's not null
/// </summary>
public void AddOptionalBoolString(string key, bool? value)
{
if (value == null)
return;
base.Add(key, value.ToString()!.ToLower());
}
/// <summary> /// <summary>
/// Set the request body. Can be used to specify a simple value or array as the body instead of an object /// Set the request body. Can be used to specify a simple value or array as the body instead of an object
@@ -640,6 +640,34 @@ namespace CryptoExchange.Net.OrderBook
return new CallResult<bool>(true); return new CallResult<bool>(true);
} }
/// <summary>
/// Wait until an update has been buffered
/// </summary>
/// <param name="timeout">Max wait time</param>
/// <param name="ct">Cancellation token</param>
/// <returns></returns>
protected async Task<CallResult<bool>> WaitUntilFirstUpdateBufferedAsync(TimeSpan timeout, CancellationToken ct)
{
var startWait = DateTime.UtcNow;
while (_processBuffer.Count == 0)
{
if (ct.IsCancellationRequested)
return new CallResult<bool>(new CancellationRequestedError());
if (DateTime.UtcNow - startWait > timeout)
return new CallResult<bool>(new ServerError(new ErrorInfo(ErrorType.OrderBookTimeout, "Timeout while waiting for data")));
try
{
await Task.Delay(20, ct).ConfigureAwait(false);
}
catch (OperationCanceledException)
{ }
}
return new CallResult<bool>(true);
}
/// <summary> /// <summary>
/// IDisposable implementation for the order book /// IDisposable implementation for the order book
/// </summary> /// </summary>
@@ -1002,7 +1030,8 @@ namespace CryptoExchange.Net.OrderBook
private SequenceNumberResult ValidateLiveSequenceNumber(long sequenceNumber) private SequenceNumberResult ValidateLiveSequenceNumber(long sequenceNumber)
{ {
if (sequenceNumber < LastSequenceNumber) if (sequenceNumber < LastSequenceNumber
&& (_firstUpdateAfterSnapshotDone || !_skipSequenceCheckFirstUpdateAfterSnapshotSet))
// Update is somehow from before the current state // Update is somehow from before the current state
return SequenceNumberResult.OutOfSync; return SequenceNumberResult.OutOfSync;
@@ -571,8 +571,12 @@ namespace CryptoExchange.Net.Sockets.Default
if (deserializationType == null) if (deserializationType == null)
{ {
// No handler found for identifier either, can't process if (!ApiClient.HandleUnhandledMessage(this, typeIdentifier, data))
_logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", typeIdentifier, Encoding.UTF8.GetString(data.ToArray())); {
// No handler found for identifier either, can't process
_logger.LogWarning("Failed to determine message type for identifier {Identifier}. Data: {Message}", typeIdentifier, Encoding.UTF8.GetString(data.ToArray()));
}
return; return;
} }
+2 -1
View File
@@ -113,7 +113,8 @@ namespace CryptoExchange.Net
/// <param name="api"></param> /// <param name="api"></param>
internal static void RegisterRestApi(string api) internal static void RegisterRestApi(string api)
{ {
_lastRestDelays[api] = new RestTimeOffset(); if (!_lastRestDelays.ContainsKey(api))
_lastRestDelays.TryAdd(api, new RestTimeOffset());
} }
/// <summary> /// <summary>
+10
View File
@@ -66,6 +66,16 @@ 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 10.2.4 - 17 Jan 2026
* Added WaitUntilFirstUpdateBufferedAsync method on SymbolOrderBook
* Added some util methods
* Added CommaSplitStringConverter
* Fixed sequence validation bug SymbolOrderBook
* Version 10.2.3 - 14 Jan 2026
* Added HandleUnhandledMessage virtual method to SocketApiClient to allow some processing for messages which couldn't be mapped via the normal way
* Fixed semaphore exception when creating a new REST client while time sync is in progress on another client
* Version 10.2.2 - 13 Jan 2026 * Version 10.2.2 - 13 Jan 2026
* Allow the same websocket connection sequence number to be recorded multiple times * Allow the same websocket connection sequence number to be recorded multiple times