mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-12 10:26:27 +00:00
wip
This commit is contained in:
parent
23bbf0ef88
commit
f83127590a
@ -26,7 +26,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
|
|
||||||
//assert
|
//assert
|
||||||
Assert.IsTrue(client.BaseAddress == "http://test.address.com/");
|
Assert.IsTrue(client.BaseAddress == "http://test.address.com/");
|
||||||
Assert.IsTrue(client.ReconnectInterval.TotalSeconds == 6);
|
Assert.IsTrue(client.ClientOptions.ReconnectInterval.TotalSeconds == 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
|
@ -21,35 +21,19 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseClient : IDisposable
|
public abstract class BaseClient : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The address of the client
|
|
||||||
/// </summary>
|
|
||||||
public string BaseAddress { get; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the exchange the client is for
|
/// The name of the exchange the client is for
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ExchangeName { get; }
|
internal string ExchangeName { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The log object
|
/// The log object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal Log log;
|
protected internal Log log;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The api proxy
|
|
||||||
/// </summary>
|
|
||||||
protected ApiProxy? apiProxy;
|
|
||||||
/// <summary>
|
|
||||||
/// The authentication provider
|
/// The authentication provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal AuthenticationProvider? authProvider;
|
protected internal AuthenticationProvider? authProvider;
|
||||||
/// <summary>
|
/// <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
|
/// The last used id, use NextId() to get the next id and up this
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected static int lastId;
|
protected static int lastId;
|
||||||
@ -68,9 +52,9 @@ namespace CryptoExchange.Net
|
|||||||
});
|
});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Last id used
|
/// Provided client options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int LastId => lastId;
|
public ClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
@ -85,13 +69,12 @@ namespace CryptoExchange.Net
|
|||||||
log.UpdateWriters(options.LogWriters);
|
log.UpdateWriters(options.LogWriters);
|
||||||
log.Level = options.LogLevel;
|
log.Level = options.LogLevel;
|
||||||
|
|
||||||
|
ClientOptions = options;
|
||||||
|
|
||||||
ExchangeName = exchangeName;
|
ExchangeName = exchangeName;
|
||||||
OutputOriginalData = options.OutputOriginalData;
|
//BaseAddress = options.BaseAddress;
|
||||||
BaseAddress = options.BaseAddress;
|
|
||||||
apiProxy = options.Proxy;
|
|
||||||
|
|
||||||
log.Write(LogLevel.Debug, $"Client configuration: {options}, CryptoExchange.Net: v{typeof(BaseClient).Assembly.GetName().Version}, {ExchangeName}.Net: v{GetType().Assembly.GetName().Version}");
|
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>
|
/// <summary>
|
||||||
@ -145,11 +128,10 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type to deserialize into</typeparam>
|
/// <typeparam name="T">The type to deserialize into</typeparam>
|
||||||
/// <param name="data">The data to deserialize</param>
|
/// <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="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>
|
/// <param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||||
/// <returns></returns>
|
/// <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);
|
var tokenResult = ValidateJson(data);
|
||||||
if (!tokenResult)
|
if (!tokenResult)
|
||||||
@ -158,7 +140,7 @@ namespace CryptoExchange.Net
|
|||||||
return new CallResult<T>(default, tokenResult.Error);
|
return new CallResult<T>(default, tokenResult.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Deserialize<T>(tokenResult.Data, checkObject, serializer, requestId);
|
return Deserialize<T>(tokenResult.Data, serializer, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -166,39 +148,16 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type to deserialize into</typeparam>
|
/// <typeparam name="T">The type to deserialize into</typeparam>
|
||||||
/// <param name="obj">The data to deserialize</param>
|
/// <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="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>
|
/// <param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||||
/// <returns></returns>
|
/// <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)
|
if (serializer == null)
|
||||||
serializer = defaultSerializer;
|
serializer = defaultSerializer;
|
||||||
|
|
||||||
try
|
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);
|
return new CallResult<T>(obj.ToObject<T>(serializer), null);
|
||||||
}
|
}
|
||||||
catch (JsonReaderException jre)
|
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
|
// 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
|
// 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);
|
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
log.Write(LogLevel.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Response received{(elapsedMilliseconds != null ? $" in {elapsedMilliseconds}" : " ")}ms: {data}");
|
log.Write(LogLevel.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Response received{(elapsedMilliseconds != null ? $" in {elapsedMilliseconds}" : " ")}ms: {data}");
|
||||||
var result = Deserialize<T>(data, null, serializer, requestId);
|
var result = Deserialize<T>(data, serializer, requestId);
|
||||||
if(OutputOriginalData)
|
if(ClientOptions.OutputOriginalData)
|
||||||
result.OriginalData = data;
|
result.OriginalData = data;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -308,116 +267,6 @@ namespace CryptoExchange.Net
|
|||||||
return await reader.ReadToEndAsync().ConfigureAwait(false);
|
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>
|
/// <summary>
|
||||||
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique across different client instances
|
/// Generate a new unique id. The id is staticly stored so it is guarenteed to be unique across different client instances
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -204,11 +204,6 @@
|
|||||||
The base for all clients, websocket client and rest client
|
The base for all clients, websocket client and rest client
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.BaseClient.BaseAddress">
|
|
||||||
<summary>
|
|
||||||
The address of the client
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:CryptoExchange.Net.BaseClient.ExchangeName">
|
<member name="P:CryptoExchange.Net.BaseClient.ExchangeName">
|
||||||
<summary>
|
<summary>
|
||||||
The name of the exchange the client is for
|
The name of the exchange the client is for
|
||||||
@ -219,26 +214,11 @@
|
|||||||
The log object
|
The log object
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="F:CryptoExchange.Net.BaseClient.apiProxy">
|
|
||||||
<summary>
|
|
||||||
The api proxy
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:CryptoExchange.Net.BaseClient.authProvider">
|
<member name="F:CryptoExchange.Net.BaseClient.authProvider">
|
||||||
<summary>
|
<summary>
|
||||||
The authentication provider
|
The authentication provider
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="F:CryptoExchange.Net.BaseClient.lastId">
|
||||||
<summary>
|
<summary>
|
||||||
The last used id, use NextId() to get the next id and up this
|
The last used id, use NextId() to get the next id and up this
|
||||||
@ -254,9 +234,9 @@
|
|||||||
A default serializer
|
A default serializer
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.BaseClient.LastId">
|
<member name="P:CryptoExchange.Net.BaseClient.ClientOptions">
|
||||||
<summary>
|
<summary>
|
||||||
Last id used
|
Provided client options
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.BaseClient.#ctor(System.String,CryptoExchange.Net.Objects.ClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
<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>
|
<param name="data">The data to parse</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</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>
|
<summary>
|
||||||
Deserialize a string into an object
|
Deserialize a string into an object
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T">The type to deserialize into</typeparam>
|
<typeparam name="T">The type to deserialize into</typeparam>
|
||||||
<param name="data">The data to deserialize</param>
|
<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="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>
|
<param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</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>
|
<summary>
|
||||||
Deserialize a JToken into an object
|
Deserialize a JToken into an object
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T">The type to deserialize into</typeparam>
|
<typeparam name="T">The type to deserialize into</typeparam>
|
||||||
<param name="obj">The data to deserialize</param>
|
<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="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>
|
<param name="requestId">Id of the request the data is returned from (used for grouping logging by request)</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
@ -1233,31 +1211,11 @@
|
|||||||
The factory for creating requests. Used for unit testing
|
The factory for creating requests. Used for unit testing
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.TotalRequestsMade">
|
||||||
<summary>
|
<summary>
|
||||||
The total amount of requests made
|
The total amount of requests made
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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)">
|
<member name="M:CryptoExchange.Net.Interfaces.IRestClient.AddRateLimiter(CryptoExchange.Net.Interfaces.IRateLimiter)">
|
||||||
<summary>
|
<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"/>.
|
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
|
Removes all rate limiters from this client
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.IRestClient.Ping(System.Threading.CancellationToken)">
|
<member name="P:CryptoExchange.Net.Interfaces.IRestClient.ClientOptions">
|
||||||
<summary>
|
<summary>
|
||||||
Ping to see if the server is reachable
|
Client options
|
||||||
</summary>
|
</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>
|
||||||
<member name="T:CryptoExchange.Net.Interfaces.ISocketClient">
|
<member name="T:CryptoExchange.Net.Interfaces.ISocketClient">
|
||||||
<summary>
|
<summary>
|
||||||
Base class for socket API implementations
|
Base class for socket API implementations
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.SocketFactory">
|
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.ClientOptions">
|
||||||
<summary>
|
<summary>
|
||||||
The factory for creating sockets. Used for unit testing
|
Client options
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.Interfaces.ISocketClient.IncomingKbps">
|
||||||
<summary>
|
<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>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Interfaces.ISocketClient.UnsubscribeAsync(CryptoExchange.Net.Sockets.UpdateSubscription)">
|
<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
|
If true, the CallResult and DataEvent objects will also include the originally received json data in the OriginalData property
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="M:CryptoExchange.Net.Objects.BaseOptions.ToString">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
</member>
|
</member>
|
||||||
@ -2281,39 +2202,11 @@
|
|||||||
Base for order book options
|
Base for order book options
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.Objects.OrderBookOptions.ChecksumValidationEnabled">
|
||||||
<summary>
|
<summary>
|
||||||
Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="T:CryptoExchange.Net.Objects.ClientOptions">
|
||||||
<summary>
|
<summary>
|
||||||
Base client options
|
Base client options
|
||||||
@ -2329,21 +2222,18 @@
|
|||||||
The api credentials
|
The api credentials
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy">
|
||||||
<summary>
|
<summary>
|
||||||
Proxy to use
|
Proxy to use
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.ClientOptions.#ctor(System.String)">
|
<member name="M:CryptoExchange.Net.Objects.ClientOptions.Copy``1(``0,``0)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
Copy the values of the def to the input
|
||||||
</summary>
|
</summary>
|
||||||
<param name="baseAddress">The base address to use</param>
|
<typeparam name="T"></typeparam>
|
||||||
|
<param name="input"></param>
|
||||||
|
<param name="def"></param>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.ClientOptions.ToString">
|
<member name="M:CryptoExchange.Net.Objects.ClientOptions.ToString">
|
||||||
<inheritdoc />
|
<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
|
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>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)">
|
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1(``0,``0)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
Copy the values of the def to the input
|
||||||
</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
|
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T"></typeparam>
|
<typeparam name="T"></typeparam>
|
||||||
<returns></returns>
|
<param name="input"></param>
|
||||||
|
<param name="def"></param>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.ToString">
|
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.ToString">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
@ -2443,18 +2321,13 @@
|
|||||||
single connection will also increase the amount of traffic on that single connection, potentially leading to issues.
|
single connection will also increase the amount of traffic on that single connection, potentially leading to issues.
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.#ctor(System.String)">
|
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.Copy``1(``0,``0)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
Copy the values of the def to the input
|
||||||
</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
|
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T"></typeparam>
|
<typeparam name="T"></typeparam>
|
||||||
<returns></returns>
|
<param name="input"></param>
|
||||||
|
<param name="def"></param>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.ToString">
|
<member name="M:CryptoExchange.Net.Objects.SocketClientOptions.ToString">
|
||||||
<inheritdoc />
|
<inheritdoc />
|
||||||
@ -2514,6 +2387,16 @@
|
|||||||
The log
|
The log
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="F:CryptoExchange.Net.OrderBook.SymbolOrderBook.bookSet">
|
||||||
<summary>
|
<summary>
|
||||||
If order book is set
|
If order book is set
|
||||||
@ -2599,10 +2482,11 @@
|
|||||||
BestBid/BesAsk returned as a pair
|
BestBid/BesAsk returned as a pair
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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>
|
<summary>
|
||||||
ctor
|
ctor
|
||||||
</summary>
|
</summary>
|
||||||
|
<param name="id"></param>
|
||||||
<param name="symbol"></param>
|
<param name="symbol"></param>
|
||||||
<param name="options"></param>
|
<param name="options"></param>
|
||||||
</member>
|
</member>
|
||||||
@ -2936,16 +2820,6 @@
|
|||||||
What request body should be set when no data is send (only used in combination with postParametersPosition.InBody)
|
What request body should be set when no data is send (only used in combination with postParametersPosition.InBody)
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.RestClient.RateLimiters">
|
||||||
<summary>
|
<summary>
|
||||||
List of rate limiters
|
List of rate limiters
|
||||||
@ -2961,6 +2835,11 @@
|
|||||||
Request headers to be sent with each request
|
Request headers to be sent with each request
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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)">
|
<member name="M:CryptoExchange.Net.RestClient.#ctor(System.String,CryptoExchange.Net.Objects.RestClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
ctor
|
||||||
@ -2980,18 +2859,6 @@
|
|||||||
Removes all rate limiters from this client
|
Removes all rate limiters from this client
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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})">
|
<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>
|
<summary>
|
||||||
Execute a request to the uri and deserialize the response into the provided type parameter
|
Execute a request to the uri and deserialize the response into the provided type parameter
|
||||||
@ -3077,35 +2944,11 @@
|
|||||||
Semaphore used while creating sockets
|
Semaphore used while creating sockets
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="P:CryptoExchange.Net.SocketClient.MaxSocketConnections">
|
||||||
<summary>
|
<summary>
|
||||||
The max amount of concurrent socket connections
|
The max amount of concurrent socket connections
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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">
|
<member name="F:CryptoExchange.Net.SocketClient.dataInterpreterBytes">
|
||||||
<summary>
|
<summary>
|
||||||
Delegate used for processing byte data received from socket connections before it is processed by handlers
|
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
|
The current kilobytes per second of data being received by all connection from this client, averaged over the last 3 seconds
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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)">
|
<member name="M:CryptoExchange.Net.SocketClient.#ctor(System.String,CryptoExchange.Net.Objects.SocketClientOptions,CryptoExchange.Net.Authentication.AuthenticationProvider)">
|
||||||
<summary>
|
<summary>
|
||||||
ctor
|
ctor
|
||||||
@ -3312,7 +3160,7 @@
|
|||||||
<param name="message"></param>
|
<param name="message"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</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>
|
<summary>
|
||||||
Add a subscription to a connection
|
Add a subscription to a connection
|
||||||
</summary>
|
</summary>
|
||||||
@ -3943,7 +3791,6 @@
|
|||||||
<param name="request"></param>
|
<param name="request"></param>
|
||||||
<param name="userSubscription"></param>
|
<param name="userSubscription"></param>
|
||||||
<param name="dataHandler"></param>
|
<param name="dataHandler"></param>
|
||||||
<param name="ct"></param>
|
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Sockets.SocketSubscription.CreateForIdentifier(System.Int32,System.String,System.Boolean,System.Action{CryptoExchange.Net.Sockets.MessageEvent})">
|
<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="identifier"></param>
|
||||||
<param name="userSubscription"></param>
|
<param name="userSubscription"></param>
|
||||||
<param name="dataHandler"></param>
|
<param name="dataHandler"></param>
|
||||||
<param name="ct"></param>
|
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:CryptoExchange.Net.Sockets.SocketSubscription.InvokeExceptionHandler(System.Exception)">
|
<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})">
|
<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 />
|
<inheritdoc />
|
||||||
</member>
|
</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>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
@ -17,31 +17,11 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IRequestFactory RequestFactory { get; set; }
|
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>
|
/// <summary>
|
||||||
/// The total amount of requests made
|
/// The total amount of requests made
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int TotalRequestsMade { get; }
|
int TotalRequestsMade { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base address of the API
|
|
||||||
/// </summary>
|
|
||||||
string BaseAddress { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Client name
|
|
||||||
/// </summary>
|
|
||||||
string ExchangeName { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a rate limiter to the client. There are 2 choices, the <see cref="RateLimiterTotal"/> and the <see cref="RateLimiterPerEndpoint"/>.
|
/// Adds a rate limiter to the client. There are 2 choices, the <see cref="RateLimiterTotal"/> and the <see cref="RateLimiterPerEndpoint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,15 +34,8 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
void RemoveRateLimiters();
|
void RemoveRateLimiters();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ping to see if the server is reachable
|
/// Client options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The roundtrip time of the ping request</returns>
|
RestClientOptions ClientOptions { get; }
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,48 +11,14 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
public interface ISocketClient: IDisposable
|
public interface ISocketClient: IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The factory for creating sockets. Used for unit testing
|
/// Client options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IWebsocketFactory SocketFactory { get; set; }
|
SocketClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time in between reconnect attempts
|
/// Incoming kilobytes per second of data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
TimeSpan ReconnectInterval { get; }
|
public double IncomingKbps { 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; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unsubscribe from a stream
|
/// Unsubscribe from a stream
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using CryptoExchange.Net.Authentication;
|
using CryptoExchange.Net.Authentication;
|
||||||
using CryptoExchange.Net.Interfaces;
|
using CryptoExchange.Net.Interfaces;
|
||||||
@ -16,7 +17,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum log level to output. Setting it to null will send all messages to the registered ILoggers.
|
/// The minimum log level to output. Setting it to null will send all messages to the registered ILoggers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LogLevel? LogLevel { get; set; } = Microsoft.Extensions.Logging.LogLevel.Information;
|
public LogLevel LogLevel { get; set; } = LogLevel.Information;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The log writers
|
/// The log writers
|
||||||
@ -28,6 +29,19 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OutputOriginalData { get; set; } = false;
|
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 />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@ -39,47 +53,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// Base for order book options
|
/// Base for order book options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class OrderBookOptions : BaseOptions
|
public class OrderBookOptions : BaseOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The name of the order book implementation
|
|
||||||
/// </summary>
|
|
||||||
public string OrderBookName { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
/// Whether or not checksum validation is enabled. Default is true, disabling will ignore checksum messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ChecksumValidationEnabled { get; set; } = true;
|
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>
|
/// <summary>
|
||||||
@ -87,7 +65,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ClientOptions : BaseOptions
|
public class ClientOptions : BaseOptions
|
||||||
{
|
{
|
||||||
private string _baseAddress;
|
private string _baseAddress = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base address of the client
|
/// The base address of the client
|
||||||
@ -97,6 +75,9 @@ namespace CryptoExchange.Net.Objects
|
|||||||
get => _baseAddress;
|
get => _baseAddress;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var newValue = value;
|
var newValue = value;
|
||||||
if (!newValue.EndsWith("/"))
|
if (!newValue.EndsWith("/"))
|
||||||
newValue += "/";
|
newValue += "/";
|
||||||
@ -109,25 +90,24 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ApiCredentials? ApiCredentials { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Proxy to use
|
/// Proxy to use
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ApiProxy? Proxy { get; set; }
|
public ApiProxy? Proxy { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// Copy the values of the def to the input
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseAddress">The base address to use</param>
|
/// <typeparam name="T"></typeparam>
|
||||||
#pragma warning disable 8618
|
/// <param name="input"></param>
|
||||||
public ClientOptions(string baseAddress)
|
/// <param name="def"></param>
|
||||||
#pragma warning restore 8618
|
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 />
|
/// <inheritdoc />
|
||||||
@ -163,44 +143,19 @@ namespace CryptoExchange.Net.Objects
|
|||||||
public HttpClient? HttpClient { get; set; }
|
public HttpClient? HttpClient { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// Copy the values of the def to the input
|
||||||
/// </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
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <returns></returns>
|
/// <param name="input"></param>
|
||||||
public T Copy<T>() where T : RestClientOptions, new()
|
/// <param name="def"></param>
|
||||||
|
public new void Copy<T>(T input, T def) where T : RestClientOptions
|
||||||
{
|
{
|
||||||
var copy = new T
|
base.Copy(input, def);
|
||||||
{
|
|
||||||
BaseAddress = BaseAddress,
|
input.HttpClient = def.HttpClient;
|
||||||
LogLevel = LogLevel,
|
input.RateLimiters = def.RateLimiters.ToList();
|
||||||
Proxy = Proxy,
|
input.RateLimitingBehaviour = def.RateLimitingBehaviour;
|
||||||
LogWriters = LogWriters,
|
input.RequestTimeout = def.RequestTimeout;
|
||||||
RateLimiters = RateLimiters,
|
|
||||||
RateLimitingBehaviour = RateLimitingBehaviour,
|
|
||||||
RequestTimeout = RequestTimeout,
|
|
||||||
HttpClient = HttpClient
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ApiCredentials != null)
|
|
||||||
copy.ApiCredentials = ApiCredentials.Copy();
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -257,36 +212,23 @@ namespace CryptoExchange.Net.Objects
|
|||||||
public int? SocketSubscriptionsCombineTarget { get; set; }
|
public int? SocketSubscriptionsCombineTarget { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// Copy the values of the def to the input
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseAddress">The base address to use</param>
|
|
||||||
public SocketClientOptions(string baseAddress) : base(baseAddress)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a copy of the options
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <returns></returns>
|
/// <param name="input"></param>
|
||||||
public T Copy<T>() where T : SocketClientOptions, new()
|
/// <param name="def"></param>
|
||||||
|
public new void Copy<T>(T input, T def) where T : SocketClientOptions
|
||||||
{
|
{
|
||||||
var copy = new T
|
base.Copy(input, def);
|
||||||
{
|
|
||||||
BaseAddress = BaseAddress,
|
|
||||||
LogLevel = LogLevel,
|
|
||||||
Proxy = Proxy,
|
|
||||||
LogWriters = LogWriters,
|
|
||||||
AutoReconnect = AutoReconnect,
|
|
||||||
ReconnectInterval = ReconnectInterval,
|
|
||||||
SocketResponseTimeout = SocketResponseTimeout,
|
|
||||||
SocketSubscriptionsCombineTarget = SocketSubscriptionsCombineTarget
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ApiCredentials != null)
|
input.AutoReconnect = def.AutoReconnect;
|
||||||
copy.ApiCredentials = ApiCredentials.Copy();
|
input.ReconnectInterval = def.ReconnectInterval;
|
||||||
|
input.MaxReconnectTries = def.MaxReconnectTries;
|
||||||
return copy;
|
input.MaxResubscribeTries = def.MaxResubscribeTries;
|
||||||
|
input.MaxConcurrentResubscriptionsPerSocket = def.MaxConcurrentResubscriptionsPerSocket;
|
||||||
|
input.SocketResponseTimeout = def.SocketResponseTimeout;
|
||||||
|
input.SocketNoDataTimeout = def.SocketNoDataTimeout;
|
||||||
|
input.SocketSubscriptionsCombineTarget = def.SocketSubscriptionsCombineTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -35,8 +35,7 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
|
|
||||||
private OrderBookStatus status;
|
private OrderBookStatus status;
|
||||||
private UpdateSubscription? subscription;
|
private UpdateSubscription? subscription;
|
||||||
private readonly bool sequencesAreConsecutive;
|
|
||||||
private readonly bool strictLevels;
|
|
||||||
private readonly bool validateChecksum;
|
private readonly bool validateChecksum;
|
||||||
|
|
||||||
private bool _stopProcessing;
|
private bool _stopProcessing;
|
||||||
@ -53,6 +52,16 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Log log;
|
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>
|
/// <summary>
|
||||||
/// If order book is set
|
/// If order book is set
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -199,9 +208,10 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
/// <param name="symbol"></param>
|
/// <param name="symbol"></param>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
protected SymbolOrderBook(string symbol, OrderBookOptions options)
|
protected SymbolOrderBook(string id, string symbol, OrderBookOptions options)
|
||||||
{
|
{
|
||||||
if (symbol == null)
|
if (symbol == null)
|
||||||
throw new ArgumentNullException(nameof(symbol));
|
throw new ArgumentNullException(nameof(symbol));
|
||||||
@ -209,13 +219,11 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
if (options == null)
|
if (options == null)
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
|
||||||
Id = options.OrderBookName;
|
Id = id;
|
||||||
processBuffer = new List<ProcessBufferRangeSequenceEntry>();
|
processBuffer = new List<ProcessBufferRangeSequenceEntry>();
|
||||||
_processQueue = new ConcurrentQueue<object>();
|
_processQueue = new ConcurrentQueue<object>();
|
||||||
_queueEvent = new AutoResetEvent(false);
|
_queueEvent = new AutoResetEvent(false);
|
||||||
|
|
||||||
sequencesAreConsecutive = options.SequenceNumbersAreConsecutive;
|
|
||||||
strictLevels = options.StrictLevels;
|
|
||||||
validateChecksum = options.ChecksumValidationEnabled;
|
validateChecksum = options.ChecksumValidationEnabled;
|
||||||
Symbol = symbol;
|
Symbol = symbol;
|
||||||
Status = OrderBookStatus.Disconnected;
|
Status = OrderBookStatus.Disconnected;
|
||||||
@ -223,7 +231,7 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
asks = new SortedList<decimal, ISymbolOrderBookEntry>();
|
asks = new SortedList<decimal, ISymbolOrderBookEntry>();
|
||||||
bids = new SortedList<decimal, ISymbolOrderBookEntry>(new DescComparer<decimal>());
|
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() };
|
var writers = options.LogWriters ?? new List<ILogger> { new DebugLogger() };
|
||||||
log.UpdateWriters(writers.ToList());
|
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)
|
/// What request body should be set when no data is send (only used in combination with postParametersPosition.InBody)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected string requestBodyEmptyContent = "{}";
|
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>
|
/// <summary>
|
||||||
/// List of rate limiters
|
/// List of rate limiters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
protected IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total requests made by this client
|
/// Total requests made by this client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,6 +77,11 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Dictionary<string, string>? StandardRequestHeaders { get; set; }
|
protected Dictionary<string, string>? StandardRequestHeaders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client options
|
||||||
|
/// </summary>
|
||||||
|
public new RestClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -95,9 +93,9 @@ namespace CryptoExchange.Net
|
|||||||
if (exchangeOptions == null)
|
if (exchangeOptions == null)
|
||||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||||
|
|
||||||
RequestTimeout = exchangeOptions.RequestTimeout;
|
ClientOptions = exchangeOptions;
|
||||||
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
RequestFactory.Configure(exchangeOptions.RequestTimeout, exchangeOptions.Proxy, exchangeOptions.HttpClient);
|
||||||
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
|
||||||
var rateLimiters = new List<IRateLimiter>();
|
var rateLimiters = new List<IRateLimiter>();
|
||||||
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
||||||
rateLimiters.Add(rateLimiter);
|
rateLimiters.Add(rateLimiter);
|
||||||
@ -126,48 +124,6 @@ namespace CryptoExchange.Net
|
|||||||
RateLimiters = new List<IRateLimiter>();
|
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>
|
/// <summary>
|
||||||
/// Execute a request to the uri and deserialize the response into the provided type parameter
|
/// Execute a request to the uri and deserialize the response into the provided type parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -210,7 +166,7 @@ namespace CryptoExchange.Net
|
|||||||
var request = ConstructRequest(uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
var request = ConstructRequest(uri, method, parameters, signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
||||||
foreach (var limiter in RateLimiters)
|
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)
|
if (!limitResult.Success)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Information, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit");
|
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)}]"));
|
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);
|
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);
|
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
|
||||||
|
|
||||||
// Not an error, so continue deserializing
|
// Not an error, so continue deserializing
|
||||||
var deserializeResult = Deserialize<T>(parseResult.Data, null, deserializer, request.RequestId);
|
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, OutputOriginalData ? data: null, deserializeResult.Data, deserializeResult.Error);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, ClientOptions.OutputOriginalData ? data: null, deserializeResult.Data, deserializeResult.Error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -287,7 +243,7 @@ namespace CryptoExchange.Net
|
|||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.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
|
else
|
||||||
|
@ -35,27 +35,10 @@ namespace CryptoExchange.Net
|
|||||||
/// Semaphore used while creating sockets
|
/// Semaphore used while creating sockets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected internal readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
|
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>
|
/// <summary>
|
||||||
/// The max amount of concurrent socket connections
|
/// The max amount of concurrent socket connections
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxSocketConnections { get; protected set; } = 9999;
|
protected int MaxSocketConnections { get; 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; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate used for processing byte data received from socket connections before it is processed by handlers
|
/// Delegate used for processing byte data received from socket connections before it is processed by handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -110,6 +93,12 @@ namespace CryptoExchange.Net
|
|||||||
return sockets.Sum(s => s.Value.Socket.IncomingKbps);
|
return sockets.Sum(s => s.Value.Socket.IncomingKbps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client options
|
||||||
|
/// </summary>
|
||||||
|
public new SocketClientOptions ClientOptions { get; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -123,14 +112,7 @@ namespace CryptoExchange.Net
|
|||||||
if (exchangeOptions == null)
|
if (exchangeOptions == null)
|
||||||
throw new ArgumentNullException(nameof(exchangeOptions));
|
throw new ArgumentNullException(nameof(exchangeOptions));
|
||||||
|
|
||||||
AutoReconnect = exchangeOptions.AutoReconnect;
|
ClientOptions = exchangeOptions;
|
||||||
ReconnectInterval = exchangeOptions.ReconnectInterval;
|
|
||||||
ResponseTimeout = exchangeOptions.SocketResponseTimeout;
|
|
||||||
SocketNoDataTimeout = exchangeOptions.SocketNoDataTimeout;
|
|
||||||
SocketCombineTarget = exchangeOptions.SocketSubscriptionsCombineTarget ?? 1;
|
|
||||||
MaxReconnectTries = exchangeOptions.MaxReconnectTries;
|
|
||||||
MaxResubscribeTries = exchangeOptions.MaxResubscribeTries;
|
|
||||||
MaxConcurrentResubscriptionsPerSocket = exchangeOptions.MaxConcurrentResubscriptionsPerSocket;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -156,7 +138,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task<CallResult<UpdateSubscription>> SubscribeAsync<T>(object? request, string? identifier, bool authenticated, Action<DataEvent<T>> dataHandler, CancellationToken ct)
|
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>
|
/// <summary>
|
||||||
@ -184,8 +166,8 @@ namespace CryptoExchange.Net
|
|||||||
socketConnection = GetSocketConnection(url, authenticated);
|
socketConnection = GetSocketConnection(url, authenticated);
|
||||||
|
|
||||||
// Add a subscription on the socket connection
|
// Add a subscription on the socket connection
|
||||||
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler, ct);
|
subscription = AddSubscription(request, identifier, true, socketConnection, dataHandler);
|
||||||
if (SocketCombineTarget == 1)
|
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
|
// Only 1 subscription per connection, so no need to wait for connection since a new subscription will create a new connection anyway
|
||||||
semaphoreSlim.Release();
|
semaphoreSlim.Release();
|
||||||
@ -251,7 +233,7 @@ namespace CryptoExchange.Net
|
|||||||
protected internal virtual async Task<CallResult<bool>> SubscribeAndWaitAsync(SocketConnection socketConnection, object request, SocketSubscription subscription)
|
protected internal virtual async Task<CallResult<bool>> SubscribeAndWaitAsync(SocketConnection socketConnection, object request, SocketSubscription subscription)
|
||||||
{
|
{
|
||||||
CallResult<object>? callResult = null;
|
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)
|
if (callResult?.Success == true)
|
||||||
subscription.Confirmed = true;
|
subscription.Confirmed = true;
|
||||||
@ -268,7 +250,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task<CallResult<T>> QueryAsync<T>(object request, bool authenticated)
|
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>
|
/// <summary>
|
||||||
@ -287,7 +269,7 @@ namespace CryptoExchange.Net
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
socketConnection = GetSocketConnection(url, authenticated);
|
socketConnection = GetSocketConnection(url, authenticated);
|
||||||
if (SocketCombineTarget == 1)
|
if (ClientOptions.SocketSubscriptionsCombineTarget == 1)
|
||||||
{
|
{
|
||||||
// Can release early when only a single sub per connection
|
// Can release early when only a single sub per connection
|
||||||
semaphoreSlim.Release();
|
semaphoreSlim.Release();
|
||||||
@ -325,7 +307,7 @@ namespace CryptoExchange.Net
|
|||||||
protected virtual async Task<CallResult<T>> QueryAndWaitAsync<T>(SocketConnection socket, object request)
|
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"));
|
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))
|
if (!HandleQueryResponse<T>(socket, request, data, out var callResult))
|
||||||
return false;
|
return false;
|
||||||
@ -445,25 +427,25 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="connection">The socket connection the handler is on</param>
|
/// <param name="connection">The socket connection the handler is on</param>
|
||||||
/// <param name="dataHandler">The handler of the data received</param>
|
/// <param name="dataHandler">The handler of the data received</param>
|
||||||
/// <returns></returns>
|
/// <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)
|
void InternalHandler(MessageEvent messageEvent)
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(string))
|
if (typeof(T) == typeof(string))
|
||||||
{
|
{
|
||||||
var stringData = (T)Convert.ChangeType(messageEvent.JsonData.ToString(), typeof(T));
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var desResult = Deserialize<T>(messageEvent.JsonData, false);
|
var desResult = Deserialize<T>(messageEvent.JsonData);
|
||||||
if (!desResult)
|
if (!desResult)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Warning, $"Socket {connection.Socket.Id} Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
|
log.Write(LogLevel.Warning, $"Socket {connection.Socket.Id} Failed to deserialize data into type {typeof(T)}: {desResult.Error}");
|
||||||
return;
|
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
|
var subscription = request == null
|
||||||
@ -499,7 +481,7 @@ namespace CryptoExchange.Net
|
|||||||
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
|
var result = socketResult.Equals(default(KeyValuePair<int, SocketConnection>)) ? null : socketResult.Value;
|
||||||
if (result != null)
|
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
|
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new
|
||||||
return result;
|
return result;
|
||||||
@ -554,10 +536,10 @@ namespace CryptoExchange.Net
|
|||||||
var socket = SocketFactory.CreateWebsocket(log, address);
|
var socket = SocketFactory.CreateWebsocket(log, address);
|
||||||
log.Write(LogLevel.Debug, $"Socket {socket.Id} new socket created for " + address);
|
log.Write(LogLevel.Debug, $"Socket {socket.Id} new socket created for " + address);
|
||||||
|
|
||||||
if (apiProxy != null)
|
if (ClientOptions.Proxy != null)
|
||||||
socket.SetProxy(apiProxy);
|
socket.SetProxy(ClientOptions.Proxy);
|
||||||
|
|
||||||
socket.Timeout = SocketNoDataTimeout;
|
socket.Timeout = ClientOptions.SocketNoDataTimeout;
|
||||||
socket.DataInterpreterBytes = dataInterpreterBytes;
|
socket.DataInterpreterBytes = dataInterpreterBytes;
|
||||||
socket.DataInterpreterString = dataInterpreterString;
|
socket.DataInterpreterString = dataInterpreterString;
|
||||||
socket.RatelimitPerSecond = RateLimitPerSocketPerSecond;
|
socket.RatelimitPerSecond = RateLimitPerSocketPerSecond;
|
||||||
|
@ -129,7 +129,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
subscriptions = new List<SocketSubscription>();
|
subscriptions = new List<SocketSubscription>();
|
||||||
Socket = socket;
|
Socket = socket;
|
||||||
|
|
||||||
Socket.Timeout = client.SocketNoDataTimeout;
|
Socket.Timeout = client.ClientOptions.SocketNoDataTimeout;
|
||||||
Socket.OnMessage += ProcessMessage;
|
Socket.OnMessage += ProcessMessage;
|
||||||
Socket.OnClose += SocketOnClose;
|
Socket.OnClose += SocketOnClose;
|
||||||
Socket.OnOpen += SocketOnOpen;
|
Socket.OnOpen += SocketOnOpen;
|
||||||
@ -183,7 +183,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Message was not a request response, check data handlers
|
// 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 (!HandleData(messageEvent) && !handledResponse)
|
||||||
{
|
{
|
||||||
if (!socketClient.UnhandledMessageExpected)
|
if (!socketClient.UnhandledMessageExpected)
|
||||||
@ -330,7 +330,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socketClient.AutoReconnect && ShouldReconnect)
|
if (socketClient.ClientOptions.AutoReconnect && ShouldReconnect)
|
||||||
{
|
{
|
||||||
if (Socket.Reconnecting)
|
if (Socket.Reconnecting)
|
||||||
return; // Already reconnecting
|
return; // Already reconnecting
|
||||||
@ -338,7 +338,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
Socket.Reconnecting = true;
|
Socket.Reconnecting = true;
|
||||||
|
|
||||||
DisconnectTime = DateTime.UtcNow;
|
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)
|
if (!lostTriggered)
|
||||||
{
|
{
|
||||||
lostTriggered = true;
|
lostTriggered = true;
|
||||||
@ -350,7 +350,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
while (ShouldReconnect)
|
while (ShouldReconnect)
|
||||||
{
|
{
|
||||||
// Wait a bit before attempting reconnect
|
// Wait a bit before attempting reconnect
|
||||||
await Task.Delay(socketClient.ReconnectInterval).ConfigureAwait(false);
|
await Task.Delay(socketClient.ClientOptions.ReconnectInterval).ConfigureAwait(false);
|
||||||
if (!ShouldReconnect)
|
if (!ShouldReconnect)
|
||||||
{
|
{
|
||||||
// Should reconnect changed to false while waiting to reconnect
|
// Should reconnect changed to false while waiting to reconnect
|
||||||
@ -363,8 +363,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
{
|
{
|
||||||
ReconnectTry++;
|
ReconnectTry++;
|
||||||
ResubscribeTry = 0;
|
ResubscribeTry = 0;
|
||||||
if (socketClient.MaxReconnectTries != null
|
if (socketClient.ClientOptions.MaxReconnectTries != null
|
||||||
&& ReconnectTry >= socketClient.MaxReconnectTries)
|
&& ReconnectTry >= socketClient.ClientOptions.MaxReconnectTries)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect after {ReconnectTry} tries, closing");
|
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect after {ReconnectTry} tries, closing");
|
||||||
ShouldReconnect = false;
|
ShouldReconnect = false;
|
||||||
@ -377,7 +377,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
break;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,8 +392,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
{
|
{
|
||||||
ResubscribeTry++;
|
ResubscribeTry++;
|
||||||
|
|
||||||
if (socketClient.MaxResubscribeTries != null &&
|
if (socketClient.ClientOptions.MaxResubscribeTries != null &&
|
||||||
ResubscribeTry >= socketClient.MaxResubscribeTries)
|
ResubscribeTry >= socketClient.ClientOptions.MaxResubscribeTries)
|
||||||
{
|
{
|
||||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to resubscribe after {ResubscribeTry} tries, closing");
|
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to resubscribe after {ResubscribeTry} tries, closing");
|
||||||
ShouldReconnect = false;
|
ShouldReconnect = false;
|
||||||
@ -405,7 +405,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (Socket.IsOpen)
|
||||||
await Socket.CloseAsync().ConfigureAwait(false);
|
await Socket.CloseAsync().ConfigureAwait(false);
|
||||||
@ -431,7 +431,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!socketClient.AutoReconnect && ShouldReconnect)
|
if (!socketClient.ClientOptions.AutoReconnect && ShouldReconnect)
|
||||||
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
_ = Task.Run(() => ConnectionClosed?.Invoke());
|
||||||
|
|
||||||
// No reconnecting needed
|
// No reconnecting needed
|
||||||
@ -472,11 +472,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
subscriptionList = subscriptions.Where(h => h.Request != null).ToList();
|
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
|
// 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 success = true;
|
||||||
var taskList = new List<Task>();
|
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)
|
if (!Socket.IsOpen)
|
||||||
continue;
|
continue;
|
||||||
|
@ -61,7 +61,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="userSubscription"></param>
|
/// <param name="userSubscription"></param>
|
||||||
/// <param name="dataHandler"></param>
|
/// <param name="dataHandler"></param>
|
||||||
/// <param name="ct"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static SocketSubscription CreateForRequest(int id, object request, bool userSubscription,
|
public static SocketSubscription CreateForRequest(int id, object request, bool userSubscription,
|
||||||
Action<MessageEvent> dataHandler)
|
Action<MessageEvent> dataHandler)
|
||||||
@ -76,7 +75,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <param name="identifier"></param>
|
/// <param name="identifier"></param>
|
||||||
/// <param name="userSubscription"></param>
|
/// <param name="userSubscription"></param>
|
||||||
/// <param name="dataHandler"></param>
|
/// <param name="dataHandler"></param>
|
||||||
/// <param name="ct"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static SocketSubscription CreateForIdentifier(int id, string identifier, bool userSubscription,
|
public static SocketSubscription CreateForIdentifier(int id, string identifier, bool userSubscription,
|
||||||
Action<MessageEvent> dataHandler)
|
Action<MessageEvent> dataHandler)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user