mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 08:53:01 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f08ed16f2a | |||
| 212d457a6a | |||
| ac5f333766 | |||
| 640e4387c1 | |||
| a16b19019f | |||
| 2443f576ac | |||
| 4fd7e44015 | |||
| a0a3bda1c5 | |||
| 6bda7a3c73 | |||
| 69a7a714cd | |||
| 48e2e6468e | |||
| 4017ac780f | |||
| a55cd1bb13 |
@@ -15,7 +15,6 @@ using CryptoExchange.Net.Requests;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static CryptoExchange.Net.Objects.RateLimiter;
|
||||
|
||||
namespace CryptoExchange.Net
|
||||
{
|
||||
@@ -558,7 +557,7 @@ namespace CryptoExchange.Net
|
||||
{
|
||||
// Handle retry after header
|
||||
var retryAfterHeader = responseHeaders.SingleOrDefault(r => r.Key.Equals("Retry-After", StringComparison.InvariantCultureIgnoreCase));
|
||||
if (!retryAfterHeader.Value.Any())
|
||||
if (retryAfterHeader.Value?.Any() != true)
|
||||
return new ServerRateLimitError(data);
|
||||
|
||||
var value = retryAfterHeader.Value.First();
|
||||
|
||||
@@ -402,7 +402,7 @@ namespace CryptoExchange.Net
|
||||
if (!authenticated || socket.Authenticated)
|
||||
return new CallResult<bool>(true);
|
||||
|
||||
_logger.Log(LogLevel.Debug, $"Attempting to authenticate {socket.SocketId}");
|
||||
_logger.Log(LogLevel.Debug, $"Socket {socket.SocketId} Attempting to authenticate");
|
||||
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
|
||||
if (!result)
|
||||
{
|
||||
@@ -414,6 +414,7 @@ namespace CryptoExchange.Net
|
||||
return new CallResult<bool>(result.Error);
|
||||
}
|
||||
|
||||
_logger.Log(LogLevel.Debug, $"Socket {socket.SocketId} authenticated");
|
||||
socket.Authenticated = true;
|
||||
return new CallResult<bool>(true);
|
||||
}
|
||||
@@ -511,7 +512,7 @@ namespace CryptoExchange.Net
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
var stringData = (T)Convert.ChangeType(messageEvent.JsonData.ToString(), typeof(T));
|
||||
dataHandler(new DataEvent<T>(stringData, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
||||
dataHandler(new DataEvent<T>(stringData, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp, null));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -522,7 +523,7 @@ namespace CryptoExchange.Net
|
||||
return;
|
||||
}
|
||||
|
||||
dataHandler(new DataEvent<T>(desResult.Data, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
||||
dataHandler(new DataEvent<T>(desResult.Data, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp, null));
|
||||
}
|
||||
|
||||
var subscription = request == null
|
||||
|
||||
@@ -109,13 +109,21 @@ namespace CryptoExchange.Net.Converters
|
||||
{
|
||||
if (token.Type == JTokenType.Null)
|
||||
value = null;
|
||||
|
||||
if (token.Type == JTokenType.Float)
|
||||
value = token.Value<decimal>();
|
||||
}
|
||||
|
||||
if ((property.PropertyType == typeof(decimal)
|
||||
if (value is decimal)
|
||||
{
|
||||
property.SetValue(result, value);
|
||||
}
|
||||
else if ((property.PropertyType == typeof(decimal)
|
||||
|| property.PropertyType == typeof(decimal?))
|
||||
&& (value != null && value.ToString().IndexOf("e", StringComparison.OrdinalIgnoreCase) >= 0))
|
||||
{
|
||||
if (decimal.TryParse(value.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out var dec))
|
||||
var v = value.ToString();
|
||||
if (decimal.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out var dec))
|
||||
property.SetValue(result, dec);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CryptoExchange.Net.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Boolean converter with support for "0"/"1" (strings)
|
||||
/// </summary>
|
||||
public class BoolConverter : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
if (Nullable.GetUnderlyingType(objectType) != null)
|
||||
return Nullable.GetUnderlyingType(objectType) == typeof(bool);
|
||||
return objectType == typeof(bool);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the JSON representation of the object.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <param name="existingValue">The existing value of object being read.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <returns>
|
||||
/// The object value.
|
||||
/// </returns>
|
||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
switch (reader.Value?.ToString().ToLower().Trim())
|
||||
{
|
||||
case "true":
|
||||
case "yes":
|
||||
case "y":
|
||||
case "1":
|
||||
case "on":
|
||||
return true;
|
||||
case "false":
|
||||
case "no":
|
||||
case "n":
|
||||
case "0":
|
||||
case "off":
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we reach here, we're pretty much going to throw an error so let's let Json.NET throw it's pretty-fied error message.
|
||||
return new JsonSerializer().Deserialize(reader, objectType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that this converter will not participate in writing results.
|
||||
/// </summary>
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param><param name="value">The value.</param><param name="serializer">The calling serializer.</param>
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,20 @@ namespace CryptoExchange.Net.Converters
|
||||
/// </summary>
|
||||
public class EnumConverter : JsonConverter
|
||||
{
|
||||
private bool _warnOnMissingEntry = true;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public EnumConverter() { }
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="warnOnMissingEntry"></param>
|
||||
public EnumConverter(bool warnOnMissingEntry)
|
||||
{
|
||||
_warnOnMissingEntry = warnOnMissingEntry;
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, List<KeyValuePair<object, string>>> _mapping = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -51,8 +65,12 @@ namespace CryptoExchange.Net.Converters
|
||||
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received empty string as enum value, but property type is not a nullable enum. EnumType: {enumType.Name}. If you think {enumType.Name} should be nullable please open an issue on the Github repo");
|
||||
}
|
||||
else
|
||||
{
|
||||
// We received an enum value but weren't able to parse it.
|
||||
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Cannot map enum value. EnumType: {enumType.Name}, Value: {reader.Value}, Known values: {string.Join(", ", mapping.Select(m => m.Value))}. If you think {reader.Value} should added please open an issue on the Github repo");
|
||||
if (_warnOnMissingEntry)
|
||||
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Cannot map enum value. EnumType: {enumType.Name}, Value: {reader.Value}, Known values: {string.Join(", ", mapping.Select(m => m.Value))}. If you think {reader.Value} should added please open an issue on the Github repo");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@@ -117,22 +135,32 @@ namespace CryptoExchange.Net.Converters
|
||||
/// <param name="enumValue"></param>
|
||||
/// <returns></returns>
|
||||
[return: NotNullIfNotNull("enumValue")]
|
||||
public static string? GetString<T>(T enumValue)
|
||||
public static string? GetString<T>(T enumValue) => GetString(typeof(T), enumValue);
|
||||
|
||||
|
||||
[return: NotNullIfNotNull("enumValue")]
|
||||
private static string? GetString(Type objectType, object? enumValue)
|
||||
{
|
||||
var objectType = typeof(T);
|
||||
objectType = Nullable.GetUnderlyingType(objectType) ?? objectType;
|
||||
|
||||
if (!_mapping.TryGetValue(objectType, out var mapping))
|
||||
mapping = AddMapping(objectType);
|
||||
|
||||
return enumValue == null ? null : (mapping.FirstOrDefault(v => v.Key.Equals(enumValue)).Value ?? enumValue.ToString());
|
||||
return enumValue == null ? null : (mapping.FirstOrDefault(v => v.Key.Equals(enumValue)).Value ?? enumValue.ToString());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
var stringValue = GetString(value);
|
||||
writer.WriteValue(stringValue);
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
var stringValue = GetString(value.GetType(), value);
|
||||
writer.WriteValue(stringValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<PackageId>CryptoExchange.Net</PackageId>
|
||||
<Authors>JKorf</Authors>
|
||||
<Description>A base package for implementing cryptocurrency API's</Description>
|
||||
<PackageVersion>6.1.0</PackageVersion>
|
||||
<AssemblyVersion>6.1.0</AssemblyVersion>
|
||||
<FileVersion>6.1.0</FileVersion>
|
||||
<PackageVersion>6.1.5</PackageVersion>
|
||||
<AssemblyVersion>6.1.5</AssemblyVersion>
|
||||
<FileVersion>6.1.5</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.1.0 - Added support for ratelimiting on socket connections, Added rest ratelimit handling and parsing, Added ServerRatelimitError error</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>6.1.5 - Added UpdateType to socket DataEvent, Added additional scenarios for BoolConverter, Updated some logging</PackageReleaseNotes>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
||||
@@ -128,7 +128,6 @@ namespace CryptoExchange.Net
|
||||
return value / 1.000000000000000000000000000000000m;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique
|
||||
/// </summary>
|
||||
|
||||
@@ -124,4 +124,19 @@
|
||||
/// </summary>
|
||||
Closest
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of the update
|
||||
/// </summary>
|
||||
public enum SocketUpdateType
|
||||
{
|
||||
/// <summary>
|
||||
/// A update
|
||||
/// </summary>
|
||||
Update,
|
||||
/// <summary>
|
||||
/// A snapshot, generally send at the start of the connection
|
||||
/// </summary>
|
||||
Snapshot
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +117,10 @@ namespace CryptoExchange.Net.Objects
|
||||
{
|
||||
int totalWaitTime = 0;
|
||||
|
||||
EndpointRateLimiter? endpointLimit;
|
||||
List<EndpointRateLimiter> endpointLimits;
|
||||
lock (_limiterLock)
|
||||
endpointLimit = _limiters.OfType<EndpointRateLimiter>().SingleOrDefault(h => h.Endpoints.Contains(endpoint) && (h.Method == null || h.Method == method));
|
||||
if(endpointLimit != null)
|
||||
endpointLimits = _limiters.OfType<EndpointRateLimiter>().Where(h => h.Endpoints.Contains(endpoint) && (h.Method == null || h.Method == method)).ToList();
|
||||
foreach (var endpointLimit in endpointLimits)
|
||||
{
|
||||
var waitResult = await ProcessTopic(logger, endpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
|
||||
if (!waitResult)
|
||||
@@ -129,7 +129,7 @@ namespace CryptoExchange.Net.Objects
|
||||
totalWaitTime += waitResult.Data;
|
||||
}
|
||||
|
||||
if (endpointLimit?.IgnoreOtherRateLimits == true)
|
||||
if (endpointLimits.Any(l => l.IgnoreOtherRateLimits))
|
||||
return new CallResult<int>(totalWaitTime);
|
||||
|
||||
List<PartialEndpointRateLimiter> partialEndpointLimits;
|
||||
@@ -169,10 +169,10 @@ namespace CryptoExchange.Net.Objects
|
||||
if(partialEndpointLimits.Any(p => p.IgnoreOtherRateLimits))
|
||||
return new CallResult<int>(totalWaitTime);
|
||||
|
||||
ApiKeyRateLimiter? apiLimit;
|
||||
List<ApiKeyRateLimiter> apiLimits;
|
||||
lock (_limiterLock)
|
||||
apiLimit = _limiters.OfType<ApiKeyRateLimiter>().SingleOrDefault(h => h.Type == RateLimitType.ApiKey);
|
||||
if (apiLimit != null)
|
||||
apiLimits = _limiters.OfType<ApiKeyRateLimiter>().Where(h => h.Type == RateLimitType.ApiKey).ToList();
|
||||
foreach (var apiLimit in apiLimits)
|
||||
{
|
||||
if(apiKey == null)
|
||||
{
|
||||
@@ -206,13 +206,13 @@ namespace CryptoExchange.Net.Objects
|
||||
}
|
||||
}
|
||||
|
||||
if ((signed || apiLimit?.OnlyForSignedRequests == false) && apiLimit?.IgnoreTotalRateLimit == true)
|
||||
if ((signed || apiLimits.All(l => !l.OnlyForSignedRequests)) && apiLimits.Any(l => l.IgnoreTotalRateLimit))
|
||||
return new CallResult<int>(totalWaitTime);
|
||||
|
||||
TotalRateLimiter? totalLimit;
|
||||
List<TotalRateLimiter> totalLimits;
|
||||
lock (_limiterLock)
|
||||
totalLimit = _limiters.OfType<TotalRateLimiter>().SingleOrDefault();
|
||||
if (totalLimit != null)
|
||||
totalLimits = _limiters.OfType<TotalRateLimiter>().ToList();
|
||||
foreach(var totalLimit in totalLimits)
|
||||
{
|
||||
var waitResult = await ProcessTopic(logger, totalLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
|
||||
if (!waitResult)
|
||||
|
||||
@@ -464,7 +464,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
{
|
||||
var pbList = _processBuffer.ToList();
|
||||
if (pbList.Count > 0)
|
||||
_logger.Log(LogLevel.Debug, $"Processing {pbList.Count} buffered updates");
|
||||
_logger.Log(LogLevel.Debug, $"{Id} Processing {pbList.Count} buffered updates");
|
||||
|
||||
foreach (var bufferEntry in pbList)
|
||||
{
|
||||
@@ -661,7 +661,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
|
||||
if (_stopProcessing)
|
||||
{
|
||||
_logger.Log(LogLevel.Trace, "Skipping message because of resubscribing");
|
||||
_logger.Log(LogLevel.Trace, $"{Id} Skipping message because of resubscribing");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
return;
|
||||
|
||||
var bytes = Parameters.Encoding.GetBytes(data);
|
||||
_logger.Log(LogLevel.Trace, $"Socket {Id} - msg {id} - Adding {bytes.Length} to send buffer");
|
||||
_logger.Log(LogLevel.Trace, $"Socket {Id} - msg {id} - Adding {bytes.Length} bytes to send buffer");
|
||||
_sendBuffer.Enqueue(new SendItem { Id = id, Weight = weight, Bytes = bytes });
|
||||
_sendEvent.Set();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using System;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
@@ -23,35 +24,23 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// </summary>
|
||||
public string? OriginalData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of update
|
||||
/// </summary>
|
||||
public SocketUpdateType? UpdateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The received data deserialized into an object
|
||||
/// </summary>
|
||||
public T Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="timestamp"></param>
|
||||
public DataEvent(T data, DateTime timestamp)
|
||||
{
|
||||
Data = data;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
internal DataEvent(T data, string? topic, DateTime timestamp)
|
||||
{
|
||||
Data = data;
|
||||
Topic = topic;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
|
||||
internal DataEvent(T data, string? topic, string? originalData, DateTime timestamp)
|
||||
internal DataEvent(T data, string? topic, string? originalData, DateTime timestamp, SocketUpdateType? updateType)
|
||||
{
|
||||
Data = data;
|
||||
Topic = topic;
|
||||
OriginalData = originalData;
|
||||
Timestamp = timestamp;
|
||||
UpdateType = updateType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,7 +51,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <returns></returns>
|
||||
public DataEvent<K> As<K>(K data)
|
||||
{
|
||||
return new DataEvent<K>(data, Topic, OriginalData, Timestamp);
|
||||
return new DataEvent<K>(data, Topic, OriginalData, Timestamp, UpdateType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,7 +63,20 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <returns></returns>
|
||||
public DataEvent<K> As<K>(K data, string? topic)
|
||||
{
|
||||
return new DataEvent<K>(data, topic, OriginalData, Timestamp);
|
||||
return new DataEvent<K>(data, topic, OriginalData, Timestamp, UpdateType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new DataEvent with data in the from of type K based on the current DataEvent. OriginalData and Timestamp will be copied over
|
||||
/// </summary>
|
||||
/// <typeparam name="K">The type of the new data</typeparam>
|
||||
/// <param name="data">The new data</param>
|
||||
/// <param name="topic">The new topic</param>
|
||||
/// <param name="updateType">The type of update</param>
|
||||
/// <returns></returns>
|
||||
public DataEvent<K> As<K>(K data, string? topic, SocketUpdateType updateType)
|
||||
{
|
||||
return new DataEvent<K>(data, topic, OriginalData, Timestamp, updateType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
var reconnectSuccessful = await ProcessReconnectAsync().ConfigureAwait(false);
|
||||
if (!reconnectSuccessful)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, $"Failed reconnect processing: {reconnectSuccessful.Error}, reconnecting again");
|
||||
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Failed reconnect processing: {reconnectSuccessful.Error}, reconnecting again");
|
||||
await _socket.ReconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
@@ -291,10 +291,13 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <param name="requestId">Id of the request sent</param>
|
||||
protected virtual void HandleRequestSent(int requestId)
|
||||
{
|
||||
var pendingRequest = _pendingRequests.SingleOrDefault(p => p.Id == requestId);
|
||||
PendingRequest pendingRequest;
|
||||
lock (_pendingRequests)
|
||||
pendingRequest = _pendingRequests.SingleOrDefault(p => p.Id == requestId);
|
||||
|
||||
if (pendingRequest == null)
|
||||
{
|
||||
_logger.Log(LogLevel.Debug, $"Socket {SocketId} - msg {requestId} - message sent, but not pending");
|
||||
_logger.Log(LogLevel.Debug, $"Socket {SocketId} - msg {requestId} - message sent, but not pending");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -345,7 +348,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
// Answer to a timed out request, unsub if it is a subscription request
|
||||
if (pendingRequest.Subscription != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, "Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
||||
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
||||
_ = ApiClient.UnsubscribeAsync(this, pendingRequest.Subscription).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -380,7 +383,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
"Data from this socket may arrive late or not at all if message processing is continuously slow.");
|
||||
}
|
||||
|
||||
_logger.Log(LogLevel.Trace, $"Socket {SocketId}{(subscription == null ? "" : " subscription " + subscription!.Id)} message processed in {(int)total.TotalMilliseconds}ms, ({(int)userProcessTime.TotalMilliseconds}ms user code)");
|
||||
_logger.Log(LogLevel.Trace, $"Socket {SocketId}{(subscription == null ? "" : " subscription " + subscription!.Id)} message processed in {(int)total.TotalMilliseconds}ms ({(int)userProcessTime.TotalMilliseconds}ms user code)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -711,7 +714,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
var result = await ApiClient.RevitalizeRequestAsync(subscription.Request!).ConfigureAwait(false);
|
||||
if (!result)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, "Failed request revitalization: " + result.Error);
|
||||
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Failed request revitalization: " + result.Error);
|
||||
return result.As<bool>(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# CryptoExchange.Net
|
||||
[](https://github.com/JKorf/CryptoExchange.Net/actions/workflows/dotnet.yml)  
|
||||
[](https://github.com/JKorf/CryptoExchange.Net/actions/workflows/dotnet.yml) [](https://www.nuget.org/packages/CryptoExchange.Net) [](https://www.nuget.org/packages/CryptoExchange.Net)
|
||||
|
||||
CryptoExchange.Net is a base package which can be used to easily implement crypto currency exchange API's in C#. This library offers base classes for creating rest and websocket clients, and includes additional features like an automatically synchronizing order book implementation, error handling and automatic reconnects on websocket connections.
|
||||
|
||||
@@ -18,7 +18,6 @@ Use one of the following following referral links to signup to a new exchange to
|
||||
[Bittrex](https://bittrex.com/discover/join?referralCode=TST-DJM-CSX)
|
||||
[Bybit](https://partner.bybit.com/b/jkorf)
|
||||
[CoinEx](https://www.coinex.com/register?refer_code=hd6gn)
|
||||
[FTX](https://ftx.com/referrals#a=31620192)
|
||||
[Huobi](https://www.huobi.com/en-us/v/register/double-invite/?inviter_id=11343840&invite_code=fxp93)
|
||||
[Kucoin](https://www.kucoin.com/ucenter/signup?rcode=RguMux)
|
||||
|
||||
@@ -32,6 +31,25 @@ 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.1.5 - 08 Oct 2023
|
||||
* Added UpdateType to socket DataEvent
|
||||
* Added additional scenarios for BoolConverter
|
||||
* Updated some logging
|
||||
|
||||
* Version 6.1.4 - 23 Sep 2023
|
||||
* Added BoolConverter
|
||||
* Added parameter for logging warning message on missing enum entry to EnumConverter
|
||||
|
||||
* Version 6.1.3 - 18 Sep 2023
|
||||
* Fix for concurrency exception in socket subscription
|
||||
|
||||
* Version 6.1.2 - 11 Sep 2023
|
||||
* Added support for multiple of the same ratelimiting type in the same rate limiter
|
||||
* Fixed nullreference on rate limit error if no Retry-After header is returned
|
||||
|
||||
* Version 6.1.1 - 04 Sep 2023
|
||||
* Fixes for json converters
|
||||
|
||||
* Version 6.1.0 - 24 Aug 2023
|
||||
* Added support for ratelimiting on socket connections
|
||||
* Added rest ratelimit handling and parsing
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ title: Home
|
||||
nav_order: 1
|
||||
---
|
||||
|
||||
[](https://github.com/JKorf/CryptoExchange.Net/actions/workflows/dotnet.yml)  
|
||||
[](https://github.com/JKorf/CryptoExchange.Net/actions/workflows/dotnet.yml) [](https://www.nuget.org/packages/CryptoExchange.Net) [](https://www.nuget.org/packages/CryptoExchange.Net)
|
||||
|
||||
The CryptoExchange.Net library is a base package for exchange API implementations. It offers base classes for creating clients for exchange API's. Basing exchange implementation on the common CryptoExchange.Net library allows for ease of implementation for new exchanges, as only the endpoints and models have to implemented, but not all systems around requests and connections, and it makes it easier for users to implement a new library in their code base as all base principles and configuration are the same for different exchanges.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user