mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-11 01:46:12 +00:00
wip
This commit is contained in:
parent
23bbf0ef88
commit
f83127590a
@ -26,7 +26,7 @@ namespace CryptoExchange.Net.UnitTests
|
||||
|
||||
//assert
|
||||
Assert.IsTrue(client.BaseAddress == "http://test.address.com/");
|
||||
Assert.IsTrue(client.ReconnectInterval.TotalSeconds == 6);
|
||||
Assert.IsTrue(client.ClientOptions.ReconnectInterval.TotalSeconds == 6);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
|
@ -21,35 +21,19 @@ namespace CryptoExchange.Net
|
||||
/// </summary>
|
||||
public abstract class BaseClient : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The address of the client
|
||||
/// </summary>
|
||||
public string BaseAddress { get; }
|
||||
/// <summary>
|
||||
/// The name of the exchange the client is for
|
||||
/// </summary>
|
||||
public string ExchangeName { get; }
|
||||
internal string ExchangeName { get; }
|
||||
/// <summary>
|
||||
/// The log object
|
||||
/// </summary>
|
||||
protected internal Log log;
|
||||
/// <summary>
|
||||
/// The api proxy
|
||||
/// </summary>
|
||||
protected ApiProxy? apiProxy;
|
||||
/// <summary>
|
||||
/// The authentication provider
|
||||
/// </summary>
|
||||
protected internal AuthenticationProvider? authProvider;
|
||||
/// <summary>
|
||||
/// Should check objects for missing properties based on the model and the received JSON
|
||||
/// </summary>
|
||||
public bool ShouldCheckObjects { get; set; }
|
||||
/// <summary>
|
||||
/// If true, the CallResult and DataEvent objects should also contain the originally received json data in the OriginalDaa property
|
||||
/// </summary>
|
||||
public bool OutputOriginalData { get; private set; }
|
||||
/// <summary>
|
||||
/// The last used id, use NextId() to get the next id and up this
|
||||
/// </summary>
|
||||
protected static int lastId;
|
||||
@ -68,9 +52,9 @@ namespace CryptoExchange.Net
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Last id used
|
||||
/// Provided client options
|
||||
/// </summary>
|
||||
public static int LastId => lastId;
|
||||
public ClientOptions ClientOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
@ -85,13 +69,12 @@ namespace CryptoExchange.Net
|
||||
log.UpdateWriters(options.LogWriters);
|
||||
log.Level = options.LogLevel;
|
||||
|
||||
ClientOptions = options;
|
||||
|
||||
ExchangeName = exchangeName;
|
||||
OutputOriginalData = options.OutputOriginalData;
|
||||
BaseAddress = options.BaseAddress;
|
||||
apiProxy = options.Proxy;
|
||||
//BaseAddress = options.BaseAddress;
|
||||
|
||||
log.Write(LogLevel.Debug, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
|
||||
ShouldCheckObjects = options.ShouldCheckObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -145,11 +128,10 @@ namespace CryptoExchange.Net
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to deserialize into</typeparam>
|
||||
/// <param name="data">The data to deserialize</param>
|
||||
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||
/// <param name="serializer">A specific serializer to use</param>
|
||||
/// <param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||
/// <returns></returns>
|
||||
protected CallResult<T> Deserialize<T>(string data, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
|
||||
protected CallResult<T> Deserialize<T>(string data, JsonSerializer? serializer = null, int? requestId = null)
|
||||
{
|
||||
var tokenResult = ValidateJson(data);
|
||||
if (!tokenResult)
|
||||
@ -158,7 +140,7 @@ namespace CryptoExchange.Net
|
||||
return new CallResult<T>(default, tokenResult.Error);
|
||||
}
|
||||
|
||||
return Deserialize<T>(tokenResult.Data, checkObject, serializer, requestId);
|
||||
return Deserialize<T>(tokenResult.Data, serializer, requestId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -166,39 +148,16 @@ namespace CryptoExchange.Net
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to deserialize into</typeparam>
|
||||
/// <param name="obj">The data to deserialize</param>
|
||||
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||
/// <param name="serializer">A specific serializer to use</param>
|
||||
/// <param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||
/// <returns></returns>
|
||||
protected CallResult<T> Deserialize<T>(JToken obj, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
|
||||
protected CallResult<T> Deserialize<T>(JToken obj, JsonSerializer? serializer = null, int? requestId = null)
|
||||
{
|
||||
if (serializer == null)
|
||||
serializer = defaultSerializer;
|
||||
|
||||
try
|
||||
{
|
||||
if ((checkObject ?? ShouldCheckObjects)&& log.Level <= LogLevel.Debug)
|
||||
{
|
||||
// This checks the input JToken object against the class it is being serialized into and outputs any missing fields
|
||||
// in either the input or the class
|
||||
try
|
||||
{
|
||||
if (obj is JObject o)
|
||||
{
|
||||
CheckObject(typeof(T), o, requestId);
|
||||
}
|
||||
else if (obj is JArray j)
|
||||
{
|
||||
if (j.HasValues && j[0] is JObject jObject)
|
||||
CheckObject(typeof(T).GetElementType(), jObject, requestId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Failed to check response data: " + (e.InnerException?.Message ?? e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
return new CallResult<T>(obj.ToObject<T>(serializer), null);
|
||||
}
|
||||
catch (JsonReaderException jre)
|
||||
@ -243,12 +202,12 @@ namespace CryptoExchange.Net
|
||||
|
||||
// If we have to output the original json data or output the data into the logging we'll have to read to full response
|
||||
// in order to log/return the json data
|
||||
if (OutputOriginalData || log.Level <= LogLevel.Debug)
|
||||
if (ClientOptions.OutputOriginalData || log.Level <= LogLevel.Debug)
|
||||
{
|
||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
log.Write(LogLevel.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Response received{(elapsedMilliseconds != null ? $" in {elapsedMilliseconds}" : " ")}ms: {data}");
|
||||
var result = Deserialize<T>(data, null, serializer, requestId);
|
||||
if(OutputOriginalData)
|
||||
var result = Deserialize<T>(data, serializer, requestId);
|
||||
if(ClientOptions.OutputOriginalData)
|
||||
result.OriginalData = data;
|
||||
return result;
|
||||
}
|
||||
@ -308,116 +267,6 @@ namespace CryptoExchange.Net
|
||||
return await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void CheckObject(Type type, JObject obj, int? requestId = null)
|
||||
{
|
||||
if (type == null)
|
||||
return;
|
||||
|
||||
if (type.GetCustomAttribute<JsonConverterAttribute>(true) != null)
|
||||
// If type has a custom JsonConverter we assume this will handle property mapping
|
||||
return;
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
return;
|
||||
|
||||
if (!obj.HasValues && type != typeof(object))
|
||||
{
|
||||
log.Write(LogLevel.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Expected `{type.Name}`, but received object was empty");
|
||||
return;
|
||||
}
|
||||
|
||||
var isDif = false;
|
||||
var properties = new List<string>();
|
||||
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var attr = prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false).FirstOrDefault();
|
||||
var ignore = prop.GetCustomAttributes(typeof(JsonIgnoreAttribute), false).FirstOrDefault();
|
||||
if (ignore != null)
|
||||
continue;
|
||||
|
||||
var propertyName = ((JsonPropertyAttribute?) attr)?.PropertyName;
|
||||
properties.Add(propertyName ?? prop.Name);
|
||||
}
|
||||
foreach (var token in obj)
|
||||
{
|
||||
var d = properties.FirstOrDefault(p => p == token.Key);
|
||||
if (d == null)
|
||||
{
|
||||
d = properties.SingleOrDefault(p => string.Equals(p, token.Key, StringComparison.CurrentCultureIgnoreCase));
|
||||
if (d == null)
|
||||
{
|
||||
if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
|
||||
{
|
||||
log.Write(LogLevel.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
|
||||
isDif = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
properties.Remove(d);
|
||||
|
||||
var propType = GetProperty(d, props)?.PropertyType;
|
||||
if (propType == null || token.Value == null)
|
||||
continue;
|
||||
if (!IsSimple(propType) && propType != typeof(DateTime))
|
||||
{
|
||||
if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject)
|
||||
CheckObject(propType.GetElementType()!, (JObject)token.Value[0]!, requestId);
|
||||
else if (token.Value is JObject o)
|
||||
CheckObject(propType, o, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var propInfo = props.First(p => p.Name == prop ||
|
||||
((JsonPropertyAttribute)p.GetCustomAttributes(typeof(JsonPropertyAttribute), false).FirstOrDefault())?.PropertyName == prop);
|
||||
var optional = propInfo.GetCustomAttributes(typeof(JsonOptionalPropertyAttribute), false).FirstOrDefault();
|
||||
if (optional != null)
|
||||
continue;
|
||||
|
||||
isDif = true;
|
||||
log.Write(LogLevel.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
|
||||
}
|
||||
|
||||
if (isDif)
|
||||
log.Write(LogLevel.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Returned data: " + obj);
|
||||
}
|
||||
|
||||
private static PropertyInfo? GetProperty(string name, IEnumerable<PropertyInfo> props)
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var attr = prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false).FirstOrDefault();
|
||||
if (attr == null)
|
||||
{
|
||||
if (string.Equals(prop.Name, name, StringComparison.CurrentCultureIgnoreCase))
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (((JsonPropertyAttribute)attr).PropertyName == name)
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsSimple(Type type)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
// nullable type, check if the nested type is simple.
|
||||
return IsSimple(type.GetGenericArguments()[0]);
|
||||
}
|
||||
return type.IsPrimitive
|
||||
|| type.IsEnum
|
||||
|| type == typeof(string)
|
||||
|| type == typeof(decimal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique across different client instances
|
||||
/// </summary>
|
||||
|
@ -204,11 +204,6 @@
|
||||
The base for all clients, websocket client and rest client
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.BaseClient.BaseAddress">
|
||||
<summary>
|
||||
The address of the client
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.BaseClient.ExchangeName">
|
||||
<summary>
|
||||
The name of the exchange the client is for
|
||||
@ -219,26 +214,11 @@
|
||||
The log object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.BaseClient.apiProxy">
|
||||
<summary>
|
||||
The api proxy
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.BaseClient.authProvider">
|
||||
<summary>
|
||||
The authentication provider
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.BaseClient.ShouldCheckObjects">
|
||||
<summary>
|
||||
Should check objects for missing properties based on the model and the received JSON
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.BaseClient.OutputOriginalData">
|
||||
<summary>
|
||||
If true, the CallResult and DataEvent objects should also contain the originally received json data in the OriginalDaa property
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.BaseClient.lastId">
|
||||
<summary>
|
||||
The last used id, use NextId() to get the next id and up this
|
||||
@ -254,9 +234,9 @@
|
||||
A default serializer
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.BaseClient.LastId">
|
||||
<member name="P:CryptoExchange.Net.BaseClient.ClientOptions">
|
||||
<summary>
|
||||
Last id used
|
||||
Provided client options
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.BaseClient.#ctor(System.String,CryptoExchange.Net.Objects.ClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||
@ -280,24 +260,22 @@
|
||||
<param name="data">The data to parse</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||
<summary>
|
||||
Deserialize a string into an object
|
||||
</summary>
|
||||
<typeparam name="T">The type to deserialize into</typeparam>
|
||||
<param name="data">The data to deserialize</param>
|
||||
<param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||
<param name="serializer">A specific serializer to use</param>
|
||||
<param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
|
||||
<summary>
|
||||
Deserialize a JToken into an object
|
||||
</summary>
|
||||
<typeparam name="T">The type to deserialize into</typeparam>
|
||||
<param name="obj">The data to deserialize</param>
|
||||
<param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
|
||||
<param name="serializer">A specific serializer to use</param>
|
||||
<param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||
<returns></returns>
|
||||
@ -1233,31 +1211,11 @@
|
||||
The factory for creating requests. Used for unit testing
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.RateLimitBehaviour">
|
||||
<summary>
|
||||
What should happen when hitting a rate limit
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.RateLimiters">
|
||||
<summary>
|
||||
List of active rate limiters
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.TotalRequestsMade">
|
||||
<summary>
|
||||
The total amount of requests made
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.BaseAddress">
|
||||
<summary>
|
||||
The base address of the API
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.ExchangeName">
|
||||
<summary>
|
||||
Client name
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Interfaces.IRestClient.AddRateLimiter(CryptoExchange.Net.Interfaces.IRateLimiter)">
|
||||
<summary>
|
||||
Adds a rate limiter to the client. There are 2 choices, the <see cref="T:CryptoExchange.Net.RateLimiter.RateLimiterTotal"/> and the <see cref="T:CryptoExchange.Net.RateLimiter.RateLimiterPerEndpoint"/>.
|
||||
@ -1269,69 +1227,24 @@
|
||||
Removes all rate limiters from this client
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Interfaces.IRestClient.Ping(System.Threading.CancellationToken)">
|
||||
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.ClientOptions">
|
||||
<summary>
|
||||
Ping to see if the server is reachable
|
||||
Client options
|
||||
</summary>
|
||||
<returns>The roundtrip time of the ping request</returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Interfaces.IRestClient.PingAsync(System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
Ping to see if the server is reachable
|
||||
</summary>
|
||||
<returns>The roundtrip time of the ping request</returns>
|
||||
</member>
|
||||
<member name="T:CryptoExchange.Net.Interfaces.ISocketClient">
|
||||
<summary>
|
||||
Base class for socket API implementations
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.SocketFactory">
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.ClientOptions">
|
||||
<summary>
|
||||
The factory for creating sockets. Used for unit testing
|
||||
Client options
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.ReconnectInterval">
|
||||
<summary>
|
||||
The time in between reconnect attempts
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.AutoReconnect">
|
||||
<summary>
|
||||
Whether the client should try to auto reconnect when losing connection
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.BaseAddress">
|
||||
<summary>
|
||||
The base address of the API
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.ResponseTimeout">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketResponseTimeout"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.SocketNoDataTimeout">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketNoDataTimeout"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxSocketConnections">
|
||||
<summary>
|
||||
The max amount of concurrent socket connections
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.SocketCombineTarget">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxReconnectTries">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxReconnectTries"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxResubscribeTries">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxResubscribeTries"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.MaxConcurrentResubscriptionsPerSocket">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.IncomingKbps">
|
||||
<summary>
|
||||
The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||
Incoming kilobytes per second of data
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Interfaces.ISocketClient.UnsubscribeAsync(CryptoExchange.Net.Sockets.UpdateSubscription)">
|
||||
@ -2273,6 +2186,14 @@
|
||||
If true, the CallResult and DataEvent objects will also include the originally received json data in the OriginalData property
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.BaseOptions.Copy``1(``0,``0)">
|
||||
<summary>
|
||||
Copy the values of the def to the input
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
<param name="input"></param>
|
||||
<param name="def"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.BaseOptions.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
@ -2281,39 +2202,11 @@
|
||||
Base for order book options
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.OrderBookOptions.OrderBookName">
|
||||
<summary>
|
||||
The name of the order book implementation
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.OrderBookOptions.ChecksumValidationEnabled">
|
||||
<summary>
|
||||
Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.OrderBookOptions.SequenceNumbersAreConsecutive">
|
||||
<summary>
|
||||
Whether each update should have a consecutive id number. Used to identify and reconnect when numbers are skipped.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.OrderBookOptions.StrictLevels">
|
||||
<summary>
|
||||
Whether or not a level should be removed from the book when it's pushed out of scope of the limit. For example with a book of limit 10,
|
||||
when a new bid level is added which makes the total amount of bids 11, should the last bid entry be removed
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.OrderBookOptions.#ctor(System.String,System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
ctor
|
||||
</summary>
|
||||
<param name="name">The name of the order book implementation</param>
|
||||
<param name="sequencesAreConsecutive">Whether each update should have a consecutive id number. Used to identify and reconnect when numbers are skipped.</param>
|
||||
<param name="strictLevels">Whether or not a level should be removed from the book when it's pushed out of scope of the limit. For example with a book of limit 10,
|
||||
when a new bid is added which makes the total amount of bids 11, should the last bid entry be removed</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.OrderBookOptions.ToString">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:CryptoExchange.Net.Objects.ClientOptions">
|
||||
<summary>
|
||||
Base client options
|
||||
@ -2329,21 +2222,18 @@
|
||||
The api credentials
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.ClientOptions.ShouldCheckObjects">
|
||||
<summary>
|
||||
Should check objects for missing properties based on the model and the received JSON
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy">
|
||||
<summary>
|
||||
Proxy to use
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.ClientOptions.#ctor(System.String)">
|
||||
<member name="M:CryptoExchange.Net.Objects.ClientOptions.Copy``1(``0,``0)">
|
||||
<summary>
|
||||
ctor
|
||||
Copy the values of the def to the input
|
||||
</summary>
|
||||
<param name="baseAddress">The base address to use</param>
|
||||
<typeparam name="T"></typeparam>
|
||||
<param name="input"></param>
|
||||
<param name="def"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.ClientOptions.ToString">
|
||||
<inheritdoc />
|
||||
@ -2373,25 +2263,13 @@
|
||||
Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored in requests and should be set on the provided HttpClient instance
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)">
|
||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1(``0,``0)">
|
||||
<summary>
|
||||
ctor
|
||||
</summary>
|
||||
<param name="baseAddress">The base address of the API</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.Net.Http.HttpClient,System.String)">
|
||||
<summary>
|
||||
ctor
|
||||
</summary>
|
||||
<param name="baseAddress">The base address of the API</param>
|
||||
<param name="httpClient">Shared http client instance</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1">
|
||||
<summary>
|
||||
Create a copy of the options
|
||||
Copy the values of the def to the input
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
<param name="input"></param>
|
||||
<param name="def"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.ToString">
|
||||
<inheritdoc />
|
||||
@ -2443,18 +2321,13 @@
|
||||
single connection will also increase the amount of traffic on that single connection, potentially leading to issues.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.#ctor(System.String)">
|
||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.Copy``1(``0,``0)">
|
||||
<summary>
|
||||
ctor
|
||||
</summary>
|
||||
<param name="baseAddress">The base address to use</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.Copy``1">
|
||||
<summary>
|
||||
Create a copy of the options
|
||||
Copy the values of the def to the input
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
<returns></returns>
|
||||
<param name="input"></param>
|
||||
<param name="def"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.ToString">
|
||||
<inheritdoc />
|
||||
@ -2514,6 +2387,16 @@
|
||||
The log
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.OrderBook.SymbolOrderBook.sequencesAreConsecutive">
|
||||
<summary>
|
||||
Whether update numbers are consecutive
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.OrderBook.SymbolOrderBook.strictLevels">
|
||||
<summary>
|
||||
Whether levels should be strictly enforced
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.OrderBook.SymbolOrderBook.bookSet">
|
||||
<summary>
|
||||
If order book is set
|
||||
@ -2599,10 +2482,11 @@
|
||||
BestBid/BesAsk returned as a pair
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.#ctor(System.String,CryptoExchange.Net.Objects.OrderBookOptions)">
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.#ctor(System.String,System.String,CryptoExchange.Net.Objects.OrderBookOptions)">
|
||||
<summary>
|
||||
ctor
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<param name="symbol"></param>
|
||||
<param name="options"></param>
|
||||
</member>
|
||||
@ -2936,16 +2820,6 @@
|
||||
What request body should be set when no data is send (only used in combination with postParametersPosition.InBody)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.RestClient.RequestTimeout">
|
||||
<summary>
|
||||
Timeout for requests. This setting is ignored when injecting a HttpClient in the options, requests timeouts should be set on the client then.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.RestClient.RateLimitBehaviour">
|
||||
<summary>
|
||||
What should happen when running into a rate limit
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.RestClient.RateLimiters">
|
||||
<summary>
|
||||
List of rate limiters
|
||||
@ -2961,6 +2835,11 @@
|
||||
Request headers to be sent with each request
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.RestClient.ClientOptions">
|
||||
<summary>
|
||||
Client options
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.RestClient.#ctor(System.String,CryptoExchange.Net.Objects.RestClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||
<summary>
|
||||
ctor
|
||||
@ -2980,18 +2859,6 @@
|
||||
Removes all rate limiters from this client
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.RestClient.Ping(System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
Ping to see if the server is reachable
|
||||
</summary>
|
||||
<returns>The roundtrip time of the ping request</returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.RestClient.PingAsync(System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
Ping to see if the server is reachable
|
||||
</summary>
|
||||
<returns>The roundtrip time of the ping request</returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.RestClient.SendRequestAsync``1(System.Uri,System.Net.Http.HttpMethod,System.Threading.CancellationToken,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,System.Boolean,System.Nullable{CryptoExchange.Net.Objects.HttpMethodParameterPosition},System.Nullable{CryptoExchange.Net.Objects.ArrayParametersSerialization},System.Int32,Newtonsoft.Json.JsonSerializer,System.Collections.Generic.Dictionary{System.String,System.String})">
|
||||
<summary>
|
||||
Execute a request to the uri and deserialize the response into the provided type parameter
|
||||
@ -3077,35 +2944,11 @@
|
||||
Semaphore used while creating sockets
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.ReconnectInterval">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.ReconnectInterval"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.AutoReconnect">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.AutoReconnect"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.ResponseTimeout">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketResponseTimeout"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.SocketNoDataTimeout">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketNoDataTimeout"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.MaxSocketConnections">
|
||||
<summary>
|
||||
The max amount of concurrent socket connections
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.SocketCombineTarget">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.MaxReconnectTries">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxReconnectTries"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.MaxResubscribeTries">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxResubscribeTries"/>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.MaxConcurrentResubscriptionsPerSocket">
|
||||
<inheritdoc cref="P:CryptoExchange.Net.Objects.SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.SocketClient.dataInterpreterBytes">
|
||||
<summary>
|
||||
Delegate used for processing byte data received from socket connections before it is processed by handlers
|
||||
@ -3157,6 +3000,11 @@
|
||||
The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.SocketClient.ClientOptions">
|
||||
<summary>
|
||||
Client options
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.SocketClient.#ctor(System.String,CryptoExchange.Net.Objects.SocketClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||
<summary>
|
||||
ctor
|
||||
@ -3312,7 +3160,7 @@
|
||||
<param name="message"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.SocketClient.AddSubscription``1(System.Object,System.String,System.Boolean,CryptoExchange.Net.Sockets.SocketConnection,System.Action{CryptoExchange.Net.Sockets.DataEvent{``0}},System.Threading.CancellationToken)">
|
||||
<member name="M:CryptoExchange.Net.SocketClient.AddSubscription``1(System.Object,System.String,System.Boolean,CryptoExchange.Net.Sockets.SocketConnection,System.Action{CryptoExchange.Net.Sockets.DataEvent{``0}})">
|
||||
<summary>
|
||||
Add a subscription to a connection
|
||||
</summary>
|
||||
@ -3943,7 +3791,6 @@
|
||||
<param name="request"></param>
|
||||
<param name="userSubscription"></param>
|
||||
<param name="dataHandler"></param>
|
||||
<param name="ct"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Sockets.SocketSubscription.CreateForIdentifier(System.Int32,System.String,System.Boolean,System.Action{CryptoExchange.Net.Sockets.MessageEvent})">
|
||||
@ -3954,7 +3801,6 @@
|
||||
<param name="identifier"></param>
|
||||
<param name="userSubscription"></param>
|
||||
<param name="dataHandler"></param>
|
||||
<param name="ct"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.Sockets.SocketSubscription.InvokeExceptionHandler(System.Exception)">
|
||||
@ -4053,5 +3899,148 @@
|
||||
<member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute">
|
||||
<summary>
|
||||
Specifies that <see langword="null"/> is allowed as an input even if the
|
||||
corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.AllowNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute">
|
||||
<summary>
|
||||
Specifies that <see langword="null"/> is disallowed as an input even if the
|
||||
corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DisallowNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute">
|
||||
<summary>
|
||||
Specifies that a method that will never return under any circumstance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute">
|
||||
<summary>
|
||||
Specifies that the method will not return if the associated <see cref="T:System.Boolean"/>
|
||||
parameter is passed the specified value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.ParameterValue">
|
||||
<summary>
|
||||
Gets the condition parameter value.
|
||||
Code after the method is considered unreachable by diagnostics if the argument
|
||||
to the associated parameter matches this value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute"/>
|
||||
class with the specified parameter value.
|
||||
</summary>
|
||||
<param name="parameterValue">
|
||||
The condition parameter value.
|
||||
Code after the method is considered unreachable by diagnostics if the argument
|
||||
to the associated parameter matches this value.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute">
|
||||
<summary>
|
||||
Specifies that an output may be <see langword="null"/> even if the
|
||||
corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute">
|
||||
<summary>
|
||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue"/>,
|
||||
the parameter may be <see langword="null"/> even if the corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue">
|
||||
<summary>
|
||||
Gets the return value condition.
|
||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes the attribute with the specified return value condition.
|
||||
</summary>
|
||||
<param name="returnValue">
|
||||
The return value condition.
|
||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullAttribute">
|
||||
<summary>
|
||||
Specifies that an output is not <see langword="null"/> even if the
|
||||
corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.NotNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute">
|
||||
<summary>
|
||||
Specifies that the output will be non-<see langword="null"/> if the
|
||||
named parameter is non-<see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.ParameterName">
|
||||
<summary>
|
||||
Gets the associated parameter name.
|
||||
The output will be non-<see langword="null"/> if the argument to the
|
||||
parameter specified is non-<see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.#ctor(System.String)">
|
||||
<summary>
|
||||
Initializes the attribute with the associated parameter name.
|
||||
</summary>
|
||||
<param name="parameterName">
|
||||
The associated parameter name.
|
||||
The output will be non-<see langword="null"/> if the argument to the
|
||||
parameter specified is non-<see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute">
|
||||
<summary>
|
||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue"/>,
|
||||
the parameter will not be <see langword="null"/> even if the corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue">
|
||||
<summary>
|
||||
Gets the return value condition.
|
||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes the attribute with the specified return value condition.
|
||||
</summary>
|
||||
<param name="returnValue">
|
||||
The return value condition.
|
||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
@ -17,31 +17,11 @@ namespace CryptoExchange.Net.Interfaces
|
||||
/// </summary>
|
||||
IRequestFactory RequestFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What should happen when hitting a rate limit
|
||||
/// </summary>
|
||||
RateLimitingBehaviour RateLimitBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of active rate limiters
|
||||
/// </summary>
|
||||
IEnumerable<IRateLimiter> RateLimiters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The total amount of requests made
|
||||
/// </summary>
|
||||
int TotalRequestsMade { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The base address of the API
|
||||
/// </summary>
|
||||
string BaseAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Client name
|
||||
/// </summary>
|
||||
string ExchangeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a rate limiter to the client. There are 2 choices, the <see cref="RateLimiterTotal"/> and the <see cref="RateLimiterPerEndpoint"/>.
|
||||
/// </summary>
|
||||
@ -54,15 +34,8 @@ namespace CryptoExchange.Net.Interfaces
|
||||
void RemoveRateLimiters();
|
||||
|
||||
/// <summary>
|
||||
/// Ping to see if the server is reachable
|
||||
/// Client options
|
||||
/// </summary>
|
||||
/// <returns>The roundtrip time of the ping request</returns>
|
||||
CallResult<long> Ping(CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Ping to see if the server is reachable
|
||||
/// </summary>
|
||||
/// <returns>The roundtrip time of the ping request</returns>
|
||||
Task<CallResult<long>> PingAsync(CancellationToken ct = default);
|
||||
RestClientOptions ClientOptions { get; }
|
||||
}
|
||||
}
|
@ -11,48 +11,14 @@ namespace CryptoExchange.Net.Interfaces
|
||||
public interface ISocketClient: IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The factory for creating sockets. Used for unit testing
|
||||
/// Client options
|
||||
/// </summary>
|
||||
IWebsocketFactory SocketFactory { get; set; }
|
||||
SocketClientOptions ClientOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The time in between reconnect attempts
|
||||
/// Incoming kilobytes per second of data
|
||||
/// </summary>
|
||||
TimeSpan ReconnectInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the client should try to auto reconnect when losing connection
|
||||
/// </summary>
|
||||
bool AutoReconnect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The base address of the API
|
||||
/// </summary>
|
||||
string BaseAddress { get; }
|
||||
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketResponseTimeout"/>
|
||||
TimeSpan ResponseTimeout { get; }
|
||||
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketNoDataTimeout"/>
|
||||
TimeSpan SocketNoDataTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The max amount of concurrent socket connections
|
||||
/// </summary>
|
||||
int MaxSocketConnections { get; }
|
||||
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
||||
int SocketCombineTarget { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxReconnectTries"/>
|
||||
int? MaxReconnectTries { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxResubscribeTries"/>
|
||||
int? MaxResubscribeTries { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||
int MaxConcurrentResubscriptionsPerSocket { get; }
|
||||
/// <summary>
|
||||
/// The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||
/// </summary>
|
||||
double IncomingKbps { get; }
|
||||
public double IncomingKbps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from a stream
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using CryptoExchange.Net.Authentication;
|
||||
using CryptoExchange.Net.Interfaces;
|
||||
@ -16,7 +17,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// <summary>
|
||||
/// The minimum log level to output. Setting it to null will send all messages to the registered ILoggers.
|
||||
/// </summary>
|
||||
public LogLevel? LogLevel { get; set; } = Microsoft.Extensions.Logging.LogLevel.Information;
|
||||
public LogLevel LogLevel { get; set; } = LogLevel.Information;
|
||||
|
||||
/// <summary>
|
||||
/// The log writers
|
||||
@ -28,6 +29,19 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public bool OutputOriginalData { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Copy the values of the def to the input
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="def"></param>
|
||||
public void Copy<T>(T input, T def) where T : BaseOptions
|
||||
{
|
||||
input.LogLevel = def.LogLevel;
|
||||
input.LogWriters = def.LogWriters.ToList();
|
||||
input.OutputOriginalData = def.OutputOriginalData;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
@ -39,47 +53,11 @@ namespace CryptoExchange.Net.Objects
|
||||
/// Base for order book options
|
||||
/// </summary>
|
||||
public class OrderBookOptions : BaseOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the order book implementation
|
||||
/// </summary>
|
||||
public string OrderBookName { get; }
|
||||
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
||||
/// </summary>
|
||||
public bool ChecksumValidationEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether each update should have a consecutive id number. Used to identify and reconnect when numbers are skipped.
|
||||
/// </summary>
|
||||
public bool SequenceNumbersAreConsecutive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not a level should be removed from the book when it's pushed out of scope of the limit. For example with a book of limit 10,
|
||||
/// when a new bid level is added which makes the total amount of bids 11, should the last bid entry be removed
|
||||
/// </summary>
|
||||
public bool StrictLevels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the order book implementation</param>
|
||||
/// <param name="sequencesAreConsecutive">Whether each update should have a consecutive id number. Used to identify and reconnect when numbers are skipped.</param>
|
||||
/// <param name="strictLevels">Whether or not a level should be removed from the book when it's pushed out of scope of the limit. For example with a book of limit 10,
|
||||
/// when a new bid is added which makes the total amount of bids 11, should the last bid entry be removed</param>
|
||||
public OrderBookOptions(string name, bool sequencesAreConsecutive, bool strictLevels)
|
||||
{
|
||||
OrderBookName = name;
|
||||
SequenceNumbersAreConsecutive = sequencesAreConsecutive;
|
||||
StrictLevels = strictLevels;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{base.ToString()}, OrderBookName: {OrderBookName}, SequenceNumbersAreConsequtive: {SequenceNumbersAreConsecutive}, StrictLevels: {StrictLevels}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -87,7 +65,7 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public class ClientOptions : BaseOptions
|
||||
{
|
||||
private string _baseAddress;
|
||||
private string _baseAddress = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The base address of the client
|
||||
@ -97,6 +75,9 @@ namespace CryptoExchange.Net.Objects
|
||||
get => _baseAddress;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
var newValue = value;
|
||||
if (!newValue.EndsWith("/"))
|
||||
newValue += "/";
|
||||
@ -109,25 +90,24 @@ namespace CryptoExchange.Net.Objects
|
||||
/// </summary>
|
||||
public ApiCredentials? ApiCredentials { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should check objects for missing properties based on the model and the received JSON
|
||||
/// </summary>
|
||||
public bool ShouldCheckObjects { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Proxy to use
|
||||
/// </summary>
|
||||
public ApiProxy? Proxy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// Copy the values of the def to the input
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">The base address to use</param>
|
||||
#pragma warning disable 8618
|
||||
public ClientOptions(string baseAddress)
|
||||
#pragma warning restore 8618
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="def"></param>
|
||||
public new void Copy<T>(T input, T def) where T : ClientOptions
|
||||
{
|
||||
BaseAddress = baseAddress;
|
||||
base.Copy(input, def);
|
||||
|
||||
input.BaseAddress = def.BaseAddress;
|
||||
input.ApiCredentials = def.ApiCredentials?.Copy();
|
||||
input.Proxy = def.Proxy;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -163,44 +143,19 @@ namespace CryptoExchange.Net.Objects
|
||||
public HttpClient? HttpClient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">The base address of the API</param>
|
||||
public RestClientOptions(string baseAddress): base(baseAddress)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">The base address of the API</param>
|
||||
/// <param name="httpClient">Shared http client instance</param>
|
||||
public RestClientOptions(HttpClient httpClient, string baseAddress) : base(baseAddress)
|
||||
{
|
||||
HttpClient = httpClient;
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a copy of the options
|
||||
/// Copy the values of the def to the input
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T Copy<T>() where T : RestClientOptions, new()
|
||||
/// <param name="input"></param>
|
||||
/// <param name="def"></param>
|
||||
public new void Copy<T>(T input, T def) where T : RestClientOptions
|
||||
{
|
||||
var copy = new T
|
||||
{
|
||||
BaseAddress = BaseAddress,
|
||||
LogLevel = LogLevel,
|
||||
Proxy = Proxy,
|
||||
LogWriters = LogWriters,
|
||||
RateLimiters = RateLimiters,
|
||||
RateLimitingBehaviour = RateLimitingBehaviour,
|
||||
RequestTimeout = RequestTimeout,
|
||||
HttpClient = HttpClient
|
||||
};
|
||||
|
||||
if (ApiCredentials != null)
|
||||
copy.ApiCredentials = ApiCredentials.Copy();
|
||||
|
||||
return copy;
|
||||
base.Copy(input, def);
|
||||
|
||||
input.HttpClient = def.HttpClient;
|
||||
input.RateLimiters = def.RateLimiters.ToList();
|
||||
input.RateLimitingBehaviour = def.RateLimitingBehaviour;
|
||||
input.RequestTimeout = def.RequestTimeout;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -257,36 +212,23 @@ namespace CryptoExchange.Net.Objects
|
||||
public int? SocketSubscriptionsCombineTarget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="baseAddress">The base address to use</param>
|
||||
public SocketClientOptions(string baseAddress) : base(baseAddress)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a copy of the options
|
||||
/// Copy the values of the def to the input
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public T Copy<T>() where T : SocketClientOptions, new()
|
||||
/// <param name="input"></param>
|
||||
/// <param name="def"></param>
|
||||
public new void Copy<T>(T input, T def) where T : SocketClientOptions
|
||||
{
|
||||
var copy = new T
|
||||
{
|
||||
BaseAddress = BaseAddress,
|
||||
LogLevel = LogLevel,
|
||||
Proxy = Proxy,
|
||||
LogWriters = LogWriters,
|
||||
AutoReconnect = AutoReconnect,
|
||||
ReconnectInterval = ReconnectInterval,
|
||||
SocketResponseTimeout = SocketResponseTimeout,
|
||||
SocketSubscriptionsCombineTarget = SocketSubscriptionsCombineTarget
|
||||
};
|
||||
base.Copy(input, def);
|
||||
|
||||
if (ApiCredentials != null)
|
||||
copy.ApiCredentials = ApiCredentials.Copy();
|
||||
|
||||
return copy;
|
||||
input.AutoReconnect = def.AutoReconnect;
|
||||
input.ReconnectInterval = def.ReconnectInterval;
|
||||
input.MaxReconnectTries = def.MaxReconnectTries;
|
||||
input.MaxResubscribeTries = def.MaxResubscribeTries;
|
||||
input.MaxConcurrentResubscriptionsPerSocket = def.MaxConcurrentResubscriptionsPerSocket;
|
||||
input.SocketResponseTimeout = def.SocketResponseTimeout;
|
||||
input.SocketNoDataTimeout = def.SocketNoDataTimeout;
|
||||
input.SocketSubscriptionsCombineTarget = def.SocketSubscriptionsCombineTarget;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -35,8 +35,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
|
||||
private OrderBookStatus status;
|
||||
private UpdateSubscription? subscription;
|
||||
private readonly bool sequencesAreConsecutive;
|
||||
private readonly bool strictLevels;
|
||||
|
||||
private readonly bool validateChecksum;
|
||||
|
||||
private bool _stopProcessing;
|
||||
@ -53,6 +52,16 @@ namespace CryptoExchange.Net.OrderBook
|
||||
/// </summary>
|
||||
protected Log log;
|
||||
|
||||
/// <summary>
|
||||
/// Whether update numbers are consecutive
|
||||
/// </summary>
|
||||
protected bool sequencesAreConsecutive;
|
||||
|
||||
/// <summary>
|
||||
/// Whether levels should be strictly enforced
|
||||
/// </summary>
|
||||
protected bool strictLevels;
|
||||
|
||||
/// <summary>
|
||||
/// If order book is set
|
||||
/// </summary>
|
||||
@ -199,9 +208,10 @@ namespace CryptoExchange.Net.OrderBook
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="symbol"></param>
|
||||
/// <param name="options"></param>
|
||||
protected SymbolOrderBook(string symbol, OrderBookOptions options)
|
||||
protected SymbolOrderBook(string id, string symbol, OrderBookOptions options)
|
||||
{
|
||||
if (symbol == null)
|
||||
throw new ArgumentNullException(nameof(symbol));
|
||||
@ -209,13 +219,11 @@ namespace CryptoExchange.Net.OrderBook
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
Id = options.OrderBookName;
|
||||
Id = id;
|
||||
processBuffer = new List<ProcessBufferRangeSequenceEntry>();
|
||||
_processQueue = new ConcurrentQueue<object>();
|
||||
_queueEvent = new AutoResetEvent(false);
|
||||
|
||||
sequencesAreConsecutive = options.SequenceNumbersAreConsecutive;
|
||||
strictLevels = options.StrictLevels;
|
||||
validateChecksum = options.ChecksumValidationEnabled;
|
||||
Symbol = symbol;
|
||||
Status = OrderBookStatus.Disconnected;
|
||||
@ -223,7 +231,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
asks = new SortedList<decimal, ISymbolOrderBookEntry>();
|
||||
bids = new SortedList<decimal, ISymbolOrderBookEntry>(new DescComparer<decimal>());
|
||||
|
||||
log = new Log(options.OrderBookName) { Level = options.LogLevel };
|
||||
log = new Log(id) { Level = options.LogLevel };
|
||||
var writers = options.LogWriters ?? new List<ILogger> { new DebugLogger() };
|
||||
log.UpdateWriters(writers.ToList());
|
||||
}
|
||||
|
@ -61,19 +61,12 @@ namespace CryptoExchange.Net
|
||||
/// What request body should be set when no data is send (only used in combination with postParametersPosition.InBody)
|
||||
/// </summary>
|
||||
protected string requestBodyEmptyContent = "{}";
|
||||
|
||||
/// <summary>
|
||||
/// Timeout for requests. This setting is ignored when injecting a HttpClient in the options, requests timeouts should be set on the client then.
|
||||
/// </summary>
|
||||
public TimeSpan RequestTimeout { get; }
|
||||
/// <summary>
|
||||
/// What should happen when running into a rate limit
|
||||
/// </summary>
|
||||
public RateLimitingBehaviour RateLimitBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of rate limiters
|
||||
/// </summary>
|
||||
public IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
||||
protected IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total requests made by this client
|
||||
/// </summary>
|
||||
@ -84,6 +77,11 @@ namespace CryptoExchange.Net
|
||||
/// </summary>
|
||||
protected Dictionary<string, string>? StandardRequestHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Client options
|
||||
/// </summary>
|
||||
public new RestClientOptions ClientOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
@ -95,9 +93,9 @@ namespace CryptoExchange.Net
|
||||
if (exchangeOptions == null)
|
||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||
|
||||
RequestTimeout = exchangeOptions.RequestTimeout;
|
||||
ClientOptions = exchangeOptions;
|
||||
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
||||
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
||||
|
||||
var rateLimiters = new List<IRateLimiter>();
|
||||
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
||||
rateLimiters.Add(rateLimiter);
|
||||
@ -126,48 +124,6 @@ namespace CryptoExchange.Net
|
||||
RateLimiters = new List<IRateLimiter>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping to see if the server is reachable
|
||||
/// </summary>
|
||||
/// <returns>The roundtrip time of the ping request</returns>
|
||||
public virtual CallResult<long> Ping(CancellationToken ct = default) => PingAsync(ct).Result;
|
||||
|
||||
/// <summary>
|
||||
/// Ping to see if the server is reachable
|
||||
/// </summary>
|
||||
/// <returns>The roundtrip time of the ping request</returns>
|
||||
public virtual async Task<CallResult<long>> PingAsync(CancellationToken ct = default)
|
||||
{
|
||||
var ping = new Ping();
|
||||
var uri = new Uri(BaseAddress);
|
||||
PingReply reply;
|
||||
|
||||
var ctRegistration = ct.Register(() => ping.SendAsyncCancel());
|
||||
try
|
||||
{
|
||||
reply = await ping.SendPingAsync(uri.Host).ConfigureAwait(false);
|
||||
}
|
||||
catch (PingException e)
|
||||
{
|
||||
if (e.InnerException == null)
|
||||
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + e.Message });
|
||||
|
||||
if (e.InnerException is SocketException exception)
|
||||
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + exception.SocketErrorCode });
|
||||
return new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + e.InnerException.Message });
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctRegistration.Dispose();
|
||||
ping.Dispose();
|
||||
}
|
||||
|
||||
if (ct.IsCancellationRequested)
|
||||
return new CallResult<long>(0, new CancellationRequestedError());
|
||||
|
||||
return reply.Status == IPStatus.Success ? new CallResult<long>(reply.RoundtripTime, null) : new CallResult<long>(0, new CantConnectError { Message = "Ping failed: " + reply.Status });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute a request to the uri and deserialize the response into the provided type parameter
|
||||
/// </summary>
|
||||
@ -210,7 +166,7 @@ namespace CryptoExchange.Net
|
||||
var request = ConstructRequest(uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
||||
foreach (var limiter in RateLimiters)
|
||||
{
|
||||
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour, credits);
|
||||
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, ClientOptions.RateLimitingBehaviour, credits);
|
||||
if (!limitResult.Success)
|
||||
{
|
||||
log.Write(LogLevel.Information, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit");
|
||||
@ -232,7 +188,7 @@ namespace CryptoExchange.Net
|
||||
paramString += " with headers " + string.Join(", ", headers.Select(h => h.Key + $"=[{string.Join(",", h.Value)}]"));
|
||||
}
|
||||
|
||||
log.Write(LogLevel.Debug, $"[{requestId}] Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null ? "" : $" via proxy {apiProxy.Host}")}");
|
||||
log.Write(LogLevel.Debug, $"[{requestId}] Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(ClientOptions.Proxy == null ? "" : $" via proxy {ClientOptions.Proxy.Host}")}");
|
||||
return await GetResponseAsync<T>(request, deserializer, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@ -277,8 +233,8 @@ namespace CryptoExchange.Net
|
||||
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
|
||||
|
||||
// Not an error, so continue deserializing
|
||||
var deserializeResult = Deserialize<T>(parseResult.Data, null, deserializer, request.RequestId);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, OutputOriginalData ? data: null, deserializeResult.Data, deserializeResult.Error);
|
||||
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
|
||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, ClientOptions.OutputOriginalData ? data: null, deserializeResult.Data, deserializeResult.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -287,7 +243,7 @@ namespace CryptoExchange.Net
|
||||
responseStream.Close();
|
||||
response.Close();
|
||||
|
||||
return new WebCallResult<T>(statusCode, headers, OutputOriginalData ? desResult.OriginalData : null, desResult.Data, desResult.Error);
|
||||
return new WebCallResult<T>(statusCode, headers, ClientOptions.OutputOriginalData ? desResult.OriginalData : null, desResult.Data, desResult.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -35,27 +35,10 @@ namespace CryptoExchange.Net
|
||||
/// Semaphore used while creating sockets
|
||||
/// </summary>
|
||||
protected internal readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
||||
|
||||
/// <inheritdoc cref="SocketClientOptions.ReconnectInterval"/>
|
||||
public TimeSpan ReconnectInterval { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.AutoReconnect"/>
|
||||
public bool AutoReconnect { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketResponseTimeout"/>
|
||||
public TimeSpan ResponseTimeout { get; }
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketNoDataTimeout"/>
|
||||
public TimeSpan SocketNoDataTimeout { get; }
|
||||
/// <summary>
|
||||
/// The max amount of concurrent socket connections
|
||||
/// </summary>
|
||||
public int MaxSocketConnections { get; protected set; } = 9999;
|
||||
/// <inheritdoc cref="SocketClientOptions.SocketSubscriptionsCombineTarget"/>
|
||||
public int SocketCombineTarget { get; protected set; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxReconnectTries"/>
|
||||
public int? MaxReconnectTries { get; protected set; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxResubscribeTries"/>
|
||||
public int? MaxResubscribeTries { get; protected set; }
|
||||
/// <inheritdoc cref="SocketClientOptions.MaxConcurrentResubscriptionsPerSocket"/>
|
||||
public int MaxConcurrentResubscriptionsPerSocket { get; protected set; }
|
||||
protected int MaxSocketConnections { get; set; } = 9999;
|
||||
/// <summary>
|
||||
/// Delegate used for processing byte data received from socket connections before it is processed by handlers
|
||||
/// </summary>
|
||||
@ -110,6 +93,12 @@ namespace CryptoExchange.Net
|
||||
return sockets.Sum(s => s.Value.Socket.IncomingKbps);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client options
|
||||
/// </summary>
|
||||
public new SocketClientOptions ClientOptions { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@ -123,14 +112,7 @@ namespace CryptoExchange.Net
|
||||
if (exchangeOptions == null)
|
||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||
|
||||
AutoReconnect = exchangeOptions.AutoReconnect;
|
||||
ReconnectInterval = exchangeOptions.ReconnectInterval;
|
||||
ResponseTimeout = exchangeOptions.SocketResponseTimeout;
|
||||
SocketNoDataTimeout = exchangeOptions.SocketNoDataTimeout;
|
||||
SocketCombineTarget = exchangeOptions.SocketSubscriptionsCombineTarget ?? 1;
|
||||
MaxReconnectTries = exchangeOptions.MaxReconnectTries;
|
||||
MaxResubscribeTries = exchangeOptions.MaxResubscribeTries;
|
||||
MaxConcurrentResubscriptionsPerSocket = exchangeOptions.MaxConcurrentResubscriptionsPerSocket;
|
||||
ClientOptions = exchangeOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -156,7 +138,7 @@ namespace CryptoExchange.Net
|
||||
/// <returns></returns>
|
||||
protected virtual Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
||||
{
|
||||
return SubscribeAsync(BaseAddress, request, identifier, authenticated, dataHandler, ct);
|
||||
return SubscribeAsync(ClientOptions.BaseAddress, request, identifier, authenticated, dataHandler, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -184,8 +166,8 @@ namespace CryptoExchange.Net
|
||||
socketConnection = GetSocketConnection(url, authenticated);
|
||||
|
||||
// Add a subscription on the socket connection
|
||||
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler, ct);
|
||||
if (SocketCombineTarget == 1)
|
||||
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
|
||||
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
|
||||
{
|
||||
// Only 1 subscription per connection, so no need to wait for connection since a new subscription will create a new connection anyway
|
||||
semaphoreSlim.Release();
|
||||
@ -251,7 +233,7 @@ namespace CryptoExchange.Net
|
||||
protected internal virtual async Task<CallResult<bool>> SubscribeAndWaitAsync(SocketConnection socketConnection, object request, SocketSubscription subscription)
|
||||
{
|
||||
CallResult<object>? callResult = null;
|
||||
await socketConnection.SendAndWaitAsync(request, ResponseTimeout, data => HandleSubscriptionResponse(socketConnection, subscription, request, data, out callResult)).ConfigureAwait(false);
|
||||
await socketConnection.SendAndWaitAsync(request, ClientOptions.SocketResponseTimeout, data => HandleSubscriptionResponse(socketConnection, subscription, request, data, out callResult)).ConfigureAwait(false);
|
||||
|
||||
if (callResult?.Success == true)
|
||||
subscription.Confirmed = true;
|
||||
@ -268,7 +250,7 @@ namespace CryptoExchange.Net
|
||||
/// <returns></returns>
|
||||
protected virtual Task<CallResult<T>> QueryAsync<T>(object request, bool authenticated)
|
||||
{
|
||||
return QueryAsync<T>(BaseAddress, request, authenticated);
|
||||
return QueryAsync<T>(ClientOptions.BaseAddress, request, authenticated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -287,7 +269,7 @@ namespace CryptoExchange.Net
|
||||
try
|
||||
{
|
||||
socketConnection = GetSocketConnection(url, authenticated);
|
||||
if (SocketCombineTarget == 1)
|
||||
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
|
||||
{
|
||||
// Can release early when only a single sub per connection
|
||||
semaphoreSlim.Release();
|
||||
@ -325,7 +307,7 @@ namespace CryptoExchange.Net
|
||||
protected virtual async Task<CallResult<T>> QueryAndWaitAsync<T>(SocketConnection socket, object request)
|
||||
{
|
||||
var dataResult = new CallResult<T>(default, new ServerError("No response on query received"));
|
||||
await socket.SendAndWaitAsync(request, ResponseTimeout, data =>
|
||||
await socket.SendAndWaitAsync(request, ClientOptions.SocketResponseTimeout, data =>
|
||||
{
|
||||
if (!HandleQueryResponse<T>(socket, request, data, out var callResult))
|
||||
return false;
|
||||
@ -445,25 +427,25 @@ namespace CryptoExchange.Net
|
||||
/// <param name="connection">The socket connection the handler is on</param>
|
||||
/// <param name="dataHandler">The handler of the data received</param>
|
||||
/// <returns></returns>
|
||||
protected virtual SocketSubscription AddSubscription<T>(object? request, string? identifier, bool userSubscription, SocketConnection connection, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
||||
protected virtual SocketSubscription AddSubscription<T>(object? request, string? identifier, bool userSubscription, SocketConnection connection, Action<DataEvent<T>> dataHandler)
|
||||
{
|
||||
void InternalHandler(MessageEvent messageEvent)
|
||||
{
|
||||
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, ClientOptions.OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
||||
return;
|
||||
}
|
||||
|
||||
var desResult = Deserialize<T>(messageEvent.JsonData, false);
|
||||
var desResult = Deserialize<T>(messageEvent.JsonData);
|
||||
if (!desResult)
|
||||
{
|
||||
log.Write(LogLevel.Warning, $"Socket {connection.Socket.Id} Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
|
||||
return;
|
||||
}
|
||||
|
||||
dataHandler(new DataEvent<T>(desResult.Data, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
||||
dataHandler(new DataEvent<T>(desResult.Data, null, ClientOptions.OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
||||
}
|
||||
|
||||
var subscription = request == null
|
||||
@ -499,7 +481,7 @@ namespace CryptoExchange.Net
|
||||
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
|
||||
if (result != null)
|
||||
{
|
||||
if (result.SubscriptionCount < SocketCombineTarget || (sockets.Count >= MaxSocketConnections && sockets.All(s => s.Value.SubscriptionCount >= SocketCombineTarget)))
|
||||
if (result.SubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (sockets.Count >= MaxSocketConnections && sockets.All(s => s.Value.SubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
|
||||
{
|
||||
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new
|
||||
return result;
|
||||
@ -554,10 +536,10 @@ namespace CryptoExchange.Net
|
||||
var socket = SocketFactory.CreateWebsocket(log, address);
|
||||
log.Write(LogLevel.Debug, $"Socket {socket.Id} new socket created for " + address);
|
||||
|
||||
if (apiProxy != null)
|
||||
socket.SetProxy(apiProxy);
|
||||
if (ClientOptions.Proxy != null)
|
||||
socket.SetProxy(ClientOptions.Proxy);
|
||||
|
||||
socket.Timeout = SocketNoDataTimeout;
|
||||
socket.Timeout = ClientOptions.SocketNoDataTimeout;
|
||||
socket.DataInterpreterBytes = dataInterpreterBytes;
|
||||
socket.DataInterpreterString = dataInterpreterString;
|
||||
socket.RatelimitPerSecond = RateLimitPerSocketPerSecond;
|
||||
|
@ -129,7 +129,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
subscriptions = new List<SocketSubscription>();
|
||||
Socket = socket;
|
||||
|
||||
Socket.Timeout = client.SocketNoDataTimeout;
|
||||
Socket.Timeout = client.ClientOptions.SocketNoDataTimeout;
|
||||
Socket.OnMessage += ProcessMessage;
|
||||
Socket.OnClose += SocketOnClose;
|
||||
Socket.OnOpen += SocketOnOpen;
|
||||
@ -183,7 +183,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
}
|
||||
|
||||
// Message was not a request response, check data handlers
|
||||
var messageEvent = new MessageEvent(this, tokenData, socketClient.OutputOriginalData ? data: null, timestamp);
|
||||
var messageEvent = new MessageEvent(this, tokenData, socketClient.ClientOptions.OutputOriginalData ? data: null, timestamp);
|
||||
if (!HandleData(messageEvent) && !handledResponse)
|
||||
{
|
||||
if (!socketClient.UnhandledMessageExpected)
|
||||
@ -330,7 +330,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
}
|
||||
}
|
||||
|
||||
if (socketClient.AutoReconnect && ShouldReconnect)
|
||||
if (socketClient.ClientOptions.AutoReconnect && ShouldReconnect)
|
||||
{
|
||||
if (Socket.Reconnecting)
|
||||
return; // Already reconnecting
|
||||
@ -338,7 +338,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
Socket.Reconnecting = true;
|
||||
|
||||
DisconnectTime = DateTime.UtcNow;
|
||||
log.Write(LogLevel.Information, $"Socket {Socket.Id} Connection lost, will try to reconnect after {socketClient.ReconnectInterval}");
|
||||
log.Write(LogLevel.Information, $"Socket {Socket.Id} Connection lost, will try to reconnect after {socketClient.ClientOptions.ReconnectInterval}");
|
||||
if (!lostTriggered)
|
||||
{
|
||||
lostTriggered = true;
|
||||
@ -350,7 +350,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
while (ShouldReconnect)
|
||||
{
|
||||
// Wait a bit before attempting reconnect
|
||||
await Task.Delay(socketClient.ReconnectInterval).ConfigureAwait(false);
|
||||
await Task.Delay(socketClient.ClientOptions.ReconnectInterval).ConfigureAwait(false);
|
||||
if (!ShouldReconnect)
|
||||
{
|
||||
// Should reconnect changed to false while waiting to reconnect
|
||||
@ -363,8 +363,8 @@ namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
ReconnectTry++;
|
||||
ResubscribeTry = 0;
|
||||
if (socketClient.MaxReconnectTries != null
|
||||
&& ReconnectTry >= socketClient.MaxReconnectTries)
|
||||
if (socketClient.ClientOptions.MaxReconnectTries != null
|
||||
&& ReconnectTry >= socketClient.ClientOptions.MaxReconnectTries)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect after {ReconnectTry} tries, closing");
|
||||
ShouldReconnect = false;
|
||||
@ -377,7 +377,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
break;
|
||||
}
|
||||
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect{(socketClient.MaxReconnectTries != null ? $", try {ReconnectTry}/{socketClient.MaxReconnectTries}": "")}");
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect{(socketClient.ClientOptions.MaxReconnectTries != null ? $", try {ReconnectTry}/{socketClient.ClientOptions.MaxReconnectTries}": "")}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -392,8 +392,8 @@ namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
ResubscribeTry++;
|
||||
|
||||
if (socketClient.MaxResubscribeTries != null &&
|
||||
ResubscribeTry >= socketClient.MaxResubscribeTries)
|
||||
if (socketClient.ClientOptions.MaxResubscribeTries != null &&
|
||||
ResubscribeTry >= socketClient.ClientOptions.MaxResubscribeTries)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to resubscribe after {ResubscribeTry} tries, closing");
|
||||
ShouldReconnect = false;
|
||||
@ -405,7 +405,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
||||
}
|
||||
else
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} resubscribing all subscriptions failed on reconnected socket{(socketClient.MaxResubscribeTries != null ? $", try {ResubscribeTry}/{socketClient.MaxResubscribeTries}" : "")}. Disconnecting and reconnecting.");
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} resubscribing all subscriptions failed on reconnected socket{(socketClient.ClientOptions.MaxResubscribeTries != null ? $", try {ResubscribeTry}/{socketClient.ClientOptions.MaxResubscribeTries}" : "")}. Disconnecting and reconnecting.");
|
||||
|
||||
if (Socket.IsOpen)
|
||||
await Socket.CloseAsync().ConfigureAwait(false);
|
||||
@ -431,7 +431,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!socketClient.AutoReconnect && ShouldReconnect)
|
||||
if (!socketClient.ClientOptions.AutoReconnect && ShouldReconnect)
|
||||
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
||||
|
||||
// No reconnecting needed
|
||||
@ -472,11 +472,11 @@ namespace CryptoExchange.Net.Sockets
|
||||
subscriptionList = subscriptions.Where(h => h.Request != null).ToList();
|
||||
|
||||
// Foreach subscription which is subscribed by a subscription request we will need to resend that request to resubscribe
|
||||
for (var i = 0; i < subscriptionList.Count; i += socketClient.MaxConcurrentResubscriptionsPerSocket)
|
||||
for (var i = 0; i < subscriptionList.Count; i += socketClient.ClientOptions.MaxConcurrentResubscriptionsPerSocket)
|
||||
{
|
||||
var success = true;
|
||||
var taskList = new List<Task>();
|
||||
foreach (var subscription in subscriptionList.Skip(i).Take(socketClient.MaxConcurrentResubscriptionsPerSocket))
|
||||
foreach (var subscription in subscriptionList.Skip(i).Take(socketClient.ClientOptions.MaxConcurrentResubscriptionsPerSocket))
|
||||
{
|
||||
if (!Socket.IsOpen)
|
||||
continue;
|
||||
|
@ -61,7 +61,6 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <param name="request"></param>
|
||||
/// <param name="userSubscription"></param>
|
||||
/// <param name="dataHandler"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public static SocketSubscription CreateForRequest(int id, object request, bool userSubscription,
|
||||
Action<MessageEvent> dataHandler)
|
||||
@ -76,7 +75,6 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// <param name="identifier"></param>
|
||||
/// <param name="userSubscription"></param>
|
||||
/// <param name="dataHandler"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
public static SocketSubscription CreateForIdentifier(int id, string identifier, bool userSubscription,
|
||||
Action<MessageEvent> dataHandler)
|
||||
|
Loading…
x
Reference in New Issue
Block a user