1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-17 19:33:07 +00:00

Squashed commit of the following:

commit 90f285d7f6bcd926ce9ca3d5832b1d70a5eae6ab
Author: JKorf <jankorf91@gmail.com>
Date:   Sun Jun 25 19:51:12 2023 +0200

    Docs

commit 72187035c703d1402b37bd2f4c3e066706f28d67
Author: JKorf <jankorf91@gmail.com>
Date:   Sat Jun 24 16:02:53 2023 +0200

    docs

commit 8411977292f1fb0b6e0705b1ad675b79a5311d90
Author: JKorf <jankorf91@gmail.com>
Date:   Fri Jun 23 18:25:15 2023 +0200

    wip

commit cb7d33aad5d2751104c8b8a6c6eadbf0d36b672c
Author: JKorf <jankorf91@gmail.com>
Date:   Fri Jun 2 19:26:26 2023 +0200

    wip

commit 4359a2d05ea1141cff516dab18f364a6ca854e18
Author: JKorf <jankorf91@gmail.com>
Date:   Wed May 31 20:51:36 2023 +0200

    wip

commit c6adb1b2f728d143f6bd667139c619581122a3c9
Author: JKorf <jankorf91@gmail.com>
Date:   Mon May 1 21:13:47 2023 +0200

    wip

commit 7fee733f82fa6ff574030452f0955c9e817647dd
Author: JKorf <jankorf91@gmail.com>
Date:   Thu Apr 27 13:02:56 2023 +0200

    wip

commit f8057313ffc9b0c31effcda71d35d105ea390971
Author: JKorf <jankorf91@gmail.com>
Date:   Mon Apr 17 21:37:51 2023 +0200

    wip
This commit is contained in:
JKorf
2023-06-25 19:58:46 +02:00
parent 19cc020852
commit 690f2a63e5
74 changed files with 1946 additions and 1826 deletions
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +13,7 @@ namespace CryptoExchange.Net.Objects
public class AsyncResetEvent : IDisposable
{
private static readonly Task<bool> _completed = Task.FromResult(true);
private readonly Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>();
private Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>();
private bool _signaled;
private readonly bool _reset;
@@ -49,7 +50,13 @@ namespace CryptoExchange.Net.Objects
var cancellationSource = new CancellationTokenSource(timeout.Value);
var registration = cancellationSource.Token.Register(() =>
{
tcs.TrySetResult(false);
lock (_waits)
{
tcs.TrySetResult(false);
// Not the cleanest but it works
_waits = new Queue<TaskCompletionSource<bool>>(_waits.Where(i => i != tcs));
}
}, useSynchronizationContext: false);
}
+78 -21
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Text;
namespace CryptoExchange.Net.Objects
{
@@ -38,6 +39,12 @@ namespace CryptoExchange.Net.Objects
{
return obj?.Success == true;
}
/// <inheritdoc />
public override string ToString()
{
return Success ? $"Success" : $"Error: {Error}";
}
}
/// <summary>
@@ -128,6 +135,24 @@ namespace CryptoExchange.Net.Objects
return new CallResult<K>(data, OriginalData, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public CallResult AsDataless()
{
return new CallResult(null);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public CallResult AsDatalessError(Error error)
{
return new CallResult(error);
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
@@ -138,6 +163,12 @@ namespace CryptoExchange.Net.Objects
{
return new CallResult<K>(default, OriginalData, error);
}
/// <inheritdoc />
public override string ToString()
{
return Success ? $"Success" : $"Error: {Error}";
}
}
/// <summary>
@@ -226,6 +257,12 @@ namespace CryptoExchange.Net.Objects
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
}
/// <inheritdoc />
public override string ToString()
{
return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}";
}
}
/// <summary>
@@ -259,6 +296,11 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public HttpStatusCode? ResponseStatusCode { get; set; }
/// <summary>
/// Length in bytes of the response
/// </summary>
public long? ResponseLength { get; set; }
/// <summary>
/// The response headers
/// </summary>
@@ -275,6 +317,7 @@ namespace CryptoExchange.Net.Objects
/// <param name="code"></param>
/// <param name="responseHeaders"></param>
/// <param name="responseTime"></param>
/// <param name="responseLength"></param>
/// <param name="originalData"></param>
/// <param name="requestUrl"></param>
/// <param name="requestBody"></param>
@@ -286,6 +329,7 @@ namespace CryptoExchange.Net.Objects
HttpStatusCode? code,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
TimeSpan? responseTime,
long? responseLength,
string? originalData,
string? requestUrl,
string? requestBody,
@@ -297,6 +341,7 @@ namespace CryptoExchange.Net.Objects
ResponseStatusCode = code;
ResponseHeaders = responseHeaders;
ResponseTime = responseTime;
ResponseLength = responseLength;
RequestUrl = requestUrl;
RequestBody = requestBody;
@@ -304,11 +349,28 @@ namespace CryptoExchange.Net.Objects
RequestMethod = requestMethod;
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public new WebCallResult AsDataless()
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public new WebCallResult AsDatalessError(Error error)
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
}
/// <summary>
/// Create a new error result
/// </summary>
/// <param name="error">The error</param>
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, default, error) { }
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, default, error) { }
/// <summary>
/// Copy the WebCallResult to a new data type
@@ -318,25 +380,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns>
public new WebCallResult<K> As<K>([AllowNull] K data)
{
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public WebCallResult AsDataless()
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public WebCallResult AsDatalessError(Error error)
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
}
/// <summary>
@@ -347,7 +391,20 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns>
public new WebCallResult<K> AsError<K>(Error error)
{
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
sb.Append(Success ? $"Success response" : $"Error response: {Error}");
if (ResponseLength != null)
sb.Append($", {ResponseLength} bytes");
if (ResponseTime != null)
sb.Append($" received in {Math.Round(ResponseTime?.TotalMilliseconds ?? 0)}ms");
return sb.ToString();
}
}
}
+287 -287
View File
@@ -1,329 +1,329 @@
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;
//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;
//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 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();
}
}
// 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>
// /// 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>
// /// The api credentials used for signing requests to this API.
// /// </summary>
// public ApiCredentials? ApiCredentials { get; set; }
/// <summary>
/// ctor
/// </summary>
public ClientOptions()
{
}
// /// <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;
// /// <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();
}
// 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();
}
// /// <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)}";
}
}
// /// <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>
// /// 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 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>
// /// 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>
//#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="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;
}
// /// <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}";
}
}
// /// <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>
// /// 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>
// /// 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>
// /// 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>
// /// 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>
// /// 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>
// /// 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>
// 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="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>();
}
// /// <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}";
}
}
// /// <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>
// /// 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>
// /// 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>
// /// 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 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 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 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 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>
// /// 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>
// 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="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;
// /// <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;
}
// 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}";
}
}
// /// <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;
}
// /// <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;
// }
}
//}
@@ -0,0 +1,20 @@
using CryptoExchange.Net.Authentication;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Options for API usage
/// </summary>
public class ApiOptions
{
/// <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; }
/// <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; }
}
}
@@ -0,0 +1,37 @@
using CryptoExchange.Net.Authentication;
using System;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Exchange options
/// </summary>
public class ExchangeOptions
{
/// <summary>
/// Proxy settings
/// </summary>
public ApiProxy? Proxy { get; set; }
/// <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 max time a request is allowed to take
/// </summary>
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(20);
/// <summary>
/// The api credentials used for signing requests to this API.
/// </summary>
public ApiCredentials? ApiCredentials { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"RequestTimeout: {RequestTimeout}, Proxy: {(Proxy == null ? "-" : "set")}, ApiCredentials: {(ApiCredentials == null ? "-" : "set")}";
}
}
}
@@ -0,0 +1,30 @@
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Base for order book options
/// </summary>
public class OrderBookOptions : ExchangeOptions
{
/// <summary>
/// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
/// </summary>
public bool ChecksumValidationEnabled { get; set; } = true;
/// <summary>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : OrderBookOptions, new()
{
return new T
{
ApiCredentials = ApiCredentials?.Copy(),
OutputOriginalData = OutputOriginalData,
ChecksumValidationEnabled = ChecksumValidationEnabled,
Proxy = Proxy,
RequestTimeout = RequestTimeout
};
}
}
}
@@ -0,0 +1,67 @@
using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces;
using System;
using System.Collections.Generic;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Http api options
/// </summary>
public class RestApiOptions : ApiOptions
{
/// <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; }
/// <summary>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T Copy<T>() where T : RestApiOptions, new()
{
return new T
{
ApiCredentials = ApiCredentials?.Copy(),
OutputOriginalData = OutputOriginalData,
AutoTimestamp = AutoTimestamp,
RateLimiters = RateLimiters,
RateLimitingBehaviour = RateLimitingBehaviour,
TimestampRecalculationInterval = TimestampRecalculationInterval
};
}
}
/// <summary>
/// Http API options
/// </summary>
/// <typeparam name="TApiCredentials"></typeparam>
public class RestApiOptions<TApiCredentials>: RestApiOptions where TApiCredentials: ApiCredentials
{
/// <summary>
/// The api credentials used for signing requests to this API.
/// </summary>
public new TApiCredentials? ApiCredentials
{
get => (TApiCredentials?)base.ApiCredentials;
set => base.ApiCredentials = value;
}
}
}
@@ -0,0 +1,83 @@
using CryptoExchange.Net.Authentication;
using System;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Options for a rest exchange client
/// </summary>
public class RestExchangeOptions: ExchangeOptions
{
/// <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>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : RestExchangeOptions, new()
{
return new T
{
OutputOriginalData = OutputOriginalData,
AutoTimestamp = AutoTimestamp,
TimestampRecalculationInterval = TimestampRecalculationInterval,
ApiCredentials = ApiCredentials?.Copy(),
Proxy = Proxy,
RequestTimeout = RequestTimeout
};
}
}
/// <summary>
/// Options for a rest exchange client
/// </summary>
/// <typeparam name="TEnvironment"></typeparam>
public class RestExchangeOptions<TEnvironment> : RestExchangeOptions where TEnvironment : TradeEnvironment
{
/// <summary>
/// Trade environment. Contains info about URL's to use to connect to the API. To swap environment select another environment for
/// the exhange's environment list or create a custom environment using either `[Exchange]Environment.CreateCustom()` or `[Exchange]Environment.[Environment]`, for example `KucoinEnvironment.TestNet` or `BinanceEnvironment.Live`
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public TEnvironment Environment { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
/// <summary>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public new T Copy<T>() where T : RestExchangeOptions<TEnvironment>, new()
{
var result = base.Copy<T>();
result.Environment = Environment;
return result;
}
}
/// <summary>
/// Options for a rest exchange client
/// </summary>
/// <typeparam name="TEnvironment"></typeparam>
/// <typeparam name="TApiCredentials"></typeparam>
public class RestExchangeOptions<TEnvironment, TApiCredentials> : RestExchangeOptions<TEnvironment> where TEnvironment : TradeEnvironment where TApiCredentials : ApiCredentials
{
/// <summary>
/// The api credentials used for signing requests to this API.
/// </summary>
public new TApiCredentials? ApiCredentials
{
get => (TApiCredentials?)base.ApiCredentials;
set => base.ApiCredentials = value;
}
}
}
@@ -0,0 +1,54 @@
using CryptoExchange.Net.Authentication;
using System;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Socket api options
/// </summary>
public class SocketApiOptions : ApiOptions
{
/// <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 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>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : SocketApiOptions, new()
{
return new T
{
ApiCredentials = ApiCredentials?.Copy(),
OutputOriginalData = OutputOriginalData,
SocketNoDataTimeout = SocketNoDataTimeout,
MaxSocketConnections = MaxSocketConnections,
};
}
}
/// <summary>
/// Socket API options
/// </summary>
/// <typeparam name="TApiCredentials"></typeparam>
public class SocketApiOptions<TApiCredentials> : SocketApiOptions where TApiCredentials : ApiCredentials
{
/// <summary>
/// The api credentials used for signing requests to this API.
/// </summary>
public new TApiCredentials? ApiCredentials
{
get => (TApiCredentials?)base.ApiCredentials;
set => base.ApiCredentials = value;
}
}
}
@@ -0,0 +1,116 @@
using CryptoExchange.Net.Authentication;
using System;
namespace CryptoExchange.Net.Objects.Options
{
/// <summary>
/// Options for a websocket exchange client
/// </summary>
public class SocketExchangeOptions : ExchangeOptions
{
/// <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 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>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : SocketExchangeOptions, new()
{
return new T
{
ApiCredentials = ApiCredentials?.Copy(),
OutputOriginalData = OutputOriginalData,
AutoReconnect = AutoReconnect,
DelayAfterConnect = DelayAfterConnect,
MaxConcurrentResubscriptionsPerSocket = MaxConcurrentResubscriptionsPerSocket,
ReconnectInterval = ReconnectInterval,
SocketNoDataTimeout = SocketNoDataTimeout,
SocketSubscriptionsCombineTarget = SocketSubscriptionsCombineTarget,
MaxSocketConnections = MaxSocketConnections,
Proxy = Proxy,
RequestTimeout = RequestTimeout
};
}
}
/// <summary>
/// Options for a socket exchange client
/// </summary>
/// <typeparam name="TEnvironment"></typeparam>
public class SocketExchangeOptions<TEnvironment> : SocketExchangeOptions where TEnvironment : TradeEnvironment
{
/// <summary>
/// Trade environment. Contains info about URL's to use to connect to the API. To swap environment select another environment for
/// the exhange's environment list or create a custom environment using either `[Exchange]Environment.CreateCustom()` or `[Exchange]Environment.[Environment]`, for example `KucoinEnvironment.TestNet` or `BinanceEnvironment.Live`
/// </summary>
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public TEnvironment Environment { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
/// <summary>
/// Create a copy of this options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public new T Copy<T>() where T : SocketExchangeOptions<TEnvironment>, new()
{
var result = base.Copy<T>();
result.Environment = Environment;
return result;
}
}
/// <summary>
/// Options for a socket exchange client
/// </summary>
/// <typeparam name="TEnvironment"></typeparam>
/// <typeparam name="TApiCredentials"></typeparam>
public class SocketExchangeOptions<TEnvironment, TApiCredentials> : SocketExchangeOptions<TEnvironment> where TEnvironment : TradeEnvironment where TApiCredentials : ApiCredentials
{
/// <summary>
/// The api credentials used for signing requests to this API.
/// </summary>
public new TApiCredentials? ApiCredentials
{
get => (TApiCredentials?)base.ApiCredentials;
set => base.ApiCredentials = value;
}
}
}
+10 -11
View File
@@ -1,5 +1,4 @@
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
@@ -101,7 +100,7 @@ namespace CryptoExchange.Net.Objects
}
/// <inheritdoc />
public async Task<CallResult<int>> LimitRequestAsync(Log log, string endpoint, HttpMethod method, bool signed, SecureString? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct)
public async Task<CallResult<int>> LimitRequestAsync(ILogger logger, string endpoint, HttpMethod method, bool signed, SecureString? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct)
{
int totalWaitTime = 0;
@@ -110,7 +109,7 @@ namespace CryptoExchange.Net.Objects
endpointLimit = Limiters.OfType<EndpointRateLimiter>().SingleOrDefault(h => h.Endpoints.Contains(endpoint) && (h.Method == null || h.Method == method));
if(endpointLimit != null)
{
var waitResult = await ProcessTopic(log, endpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, endpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -138,7 +137,7 @@ namespace CryptoExchange.Net.Objects
}
}
var waitResult = await ProcessTopic(log, thisEndpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, thisEndpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -146,7 +145,7 @@ namespace CryptoExchange.Net.Objects
}
else
{
var waitResult = await ProcessTopic(log, partialEndpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, partialEndpointLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -166,7 +165,7 @@ namespace CryptoExchange.Net.Objects
{
if (!apiLimit.OnlyForSignedRequests)
{
var waitResult = await ProcessTopic(log, apiLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, apiLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -186,7 +185,7 @@ namespace CryptoExchange.Net.Objects
}
}
var waitResult = await ProcessTopic(log, thisApiLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, thisApiLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -202,7 +201,7 @@ namespace CryptoExchange.Net.Objects
totalLimit = Limiters.OfType<TotalRateLimiter>().SingleOrDefault();
if (totalLimit != null)
{
var waitResult = await ProcessTopic(log, totalLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
var waitResult = await ProcessTopic(logger, totalLimit, endpoint, requestWeight, limitBehaviour, ct).ConfigureAwait(false);
if (!waitResult)
return waitResult;
@@ -212,7 +211,7 @@ namespace CryptoExchange.Net.Objects
return new CallResult<int>(totalWaitTime);
}
private static async Task<CallResult<int>> ProcessTopic(Log log, Limiter historyTopic, string endpoint, int requestWeight, RateLimitingBehaviour limitBehaviour, CancellationToken ct)
private static async Task<CallResult<int>> ProcessTopic(ILogger logger, Limiter historyTopic, string endpoint, int requestWeight, RateLimitingBehaviour limitBehaviour, CancellationToken ct)
{
var sw = Stopwatch.StartNew();
try
@@ -256,11 +255,11 @@ namespace CryptoExchange.Net.Objects
{
historyTopic.Semaphore.Release();
var msg = $"Request to {endpoint} failed because of rate limit `{historyTopic.Type}`. Current weight: {currentWeight}/{historyTopic.Limit}, request weight: {requestWeight}";
log.Write(LogLevel.Warning, msg);
logger.Log(LogLevel.Warning, msg);
return new CallResult<int>(new RateLimitError(msg));
}
log.Write(LogLevel.Information, $"Request to {endpoint} waiting {thisWaitTime}ms for rate limit `{historyTopic.Type}`. Current weight: {currentWeight}/{historyTopic.Limit}, request weight: {requestWeight}");
logger.Log(LogLevel.Information, $"Request to {endpoint} waiting {thisWaitTime}ms for rate limit `{historyTopic.Type}`. Current weight: {currentWeight}/{historyTopic.Limit}, request weight: {requestWeight}");
try
{
await Task.Delay(thisWaitTime, ct).ConfigureAwait(false);
+6 -7
View File
@@ -1,6 +1,5 @@
using System;
using System.Threading;
using CryptoExchange.Net.Logging;
using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Objects
@@ -45,7 +44,7 @@ namespace CryptoExchange.Net.Objects
/// <summary>
/// Logger
/// </summary>
public Log Log { get; }
public ILogger Logger { get; }
/// <summary>
/// Should synchronize time
/// </summary>
@@ -62,13 +61,13 @@ namespace CryptoExchange.Net.Objects
/// <summary>
/// ctor
/// </summary>
/// <param name="log"></param>
/// <param name="logger"></param>
/// <param name="recalculationInterval"></param>
/// <param name="syncTime"></param>
/// <param name="syncState"></param>
public TimeSyncInfo(Log log, bool syncTime, TimeSpan recalculationInterval, TimeSyncState syncState)
public TimeSyncInfo(ILogger logger, bool syncTime, TimeSpan recalculationInterval, TimeSyncState syncState)
{
Log = log;
Logger = logger;
SyncTime = syncTime;
RecalculationInterval = recalculationInterval;
TimeSyncState = syncState;
@@ -83,12 +82,12 @@ namespace CryptoExchange.Net.Objects
TimeSyncState.LastSyncTime = DateTime.UtcNow;
if (offset.TotalMilliseconds > 0 && offset.TotalMilliseconds < 500)
{
Log.Write(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset within limits, set offset to 0ms");
Logger.Log(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset within limits, set offset to 0ms");
TimeSyncState.TimeOffset = TimeSpan.Zero;
}
else
{
Log.Write(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset set to {Math.Round(offset.TotalMilliseconds)}ms");
Logger.Log(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset set to {Math.Round(offset.TotalMilliseconds)}ms");
TimeSyncState.TimeOffset = offset;
}
}
+52
View File
@@ -0,0 +1,52 @@
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// Trace logger provider for creating trace loggers
/// </summary>
public class TraceLoggerProvider : ILoggerProvider
{
/// <inheritdoc />
public ILogger CreateLogger(string categoryName) => new TraceLogger(categoryName);
/// <inheritdoc />
public void Dispose() { }
}
/// <summary>
/// Trace logger
/// </summary>
public class TraceLogger : ILogger
{
private string? _categoryName;
private LogLevel _logLevel;
/// <summary>
/// ctor
/// </summary>
/// <param name="categoryName"></param>
/// <param name="level"></param>
public TraceLogger(string? categoryName = null, LogLevel level = LogLevel.Trace)
{
_categoryName = categoryName;
_logLevel = level;
}
/// <inheritdoc />
public IDisposable BeginScope<TState>(TState state) => null!;
/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel) => (int)logLevel < (int)_logLevel;
/// <inheritdoc />
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if ((int)logLevel < (int)_logLevel)
return;
var logMessage = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | {logLevel} | {(_categoryName == null ? "" : $"{_categoryName} | ")}{formatter(state, exception)}";
Trace.WriteLine(logMessage);
}
}
}
@@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// Trade environment names
/// </summary>
public static class TradeEnvironmentNames
{
/// <summary>
/// Live environment
/// </summary>
public const string Live = "live";
/// <summary>
/// Testnet environment
/// </summary>
public const string Testnet = "testnet";
}
/// <summary>
/// Trade environment. Contains info about URL's to use to connect to the API. To swap environment select another environment for
/// the echange's environment list or create a custom environment using either `[Exchange]Environment.CreateCustom()` or `[Exchange]Environment.[Environment]`, for example `KucoinEnvironment.TestNet` or `BinanceEnvironment.Live`
/// </summary>
public class TradeEnvironment
{
/// <summary>
/// Name of the environment
/// </summary>
public string EnvironmentName { get; init; }
/// <summary>
/// </summary>
/// <param name="name"></param>
protected TradeEnvironment(string name)
{
EnvironmentName = name;
}
}
}