mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 17:03:10 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74f73dc790 | |||
| 0527a8a76e | |||
| c693eb8c02 | |||
| 3eb28c7fed | |||
| 618c4922b9 | |||
| c81b15861d | |||
| 4a5832cccd | |||
| 4e47c4cbdf | |||
| 2af1520ecc | |||
| cf397af3ab | |||
| a1479705e2 | |||
| 175e23f110 | |||
| 9b7019ded2 | |||
| 7904aa9ba7 | |||
| 3fe6db589f | |||
| 625dccbbe4 | |||
| e650771d16 | |||
| 3dad28b19d | |||
| 2b9fda985e | |||
| ff8759409b | |||
| 0d9627c13f | |||
| 0179fd7e2a | |||
| b8d0b0cf95 | |||
| 73c42bd452 | |||
| 290be7f5e0 | |||
| 0be1bb16e3 | |||
| 8605196390 | |||
| 460dd97537 | |||
| 1ec5984fad | |||
| 8260c2661d | |||
| 591c1dd405 | |||
| 0164cdfcc4 | |||
| 23a6cfff87 | |||
| fdcdb90a5f |
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 8.0.x
|
dotnet-version: 9.0.x
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#if !NETSTANDARD2_1
|
#if NETSTANDARD2_0
|
||||||
namespace System.Diagnostics.CodeAnalysis
|
namespace System.Diagnostics.CodeAnalysis
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
var rsa = RSA.Create();
|
var rsa = RSA.Create();
|
||||||
if (_credentials.CredentialType == ApiCredentialsType.RsaPem)
|
if (_credentials.CredentialType == ApiCredentialsType.RsaPem)
|
||||||
{
|
{
|
||||||
#if NETSTANDARD2_1_OR_GREATER
|
#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER
|
||||||
// Read from pem private key
|
// Read from pem private key
|
||||||
var key = _credentials.Secret!
|
var key = _credentials.Secret!
|
||||||
.Replace("\n", "")
|
.Replace("\n", "")
|
||||||
@@ -403,10 +403,14 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected static string BytesToHexString(byte[] buff)
|
protected static string BytesToHexString(byte[] buff)
|
||||||
{
|
{
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
return Convert.ToHexString(buff);
|
||||||
|
#else
|
||||||
var result = string.Empty;
|
var result = string.Empty;
|
||||||
foreach (var t in buff)
|
foreach (var t in buff)
|
||||||
result += t.ToString("X2");
|
result += t.ToString("X2");
|
||||||
return result;
|
return result;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -439,6 +443,16 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return DateTimeConverter.ConvertToMilliseconds(GetTimestamp(apiClient)).Value.ToString(CultureInfo.InvariantCulture);
|
return DateTimeConverter.ConvertToMilliseconds(GetTimestamp(apiClient)).Value.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get millisecond timestamp as a long including the time sync offset from the api client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="apiClient"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected long GetMillisecondTimestampLong(RestApiClient apiClient)
|
||||||
|
{
|
||||||
|
return DateTimeConverter.ConvertToMilliseconds(GetTimestamp(apiClient)).Value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return the serialized request body
|
/// Return the serialized request body
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace CryptoExchange.Net.Caching
|
|||||||
/// <returns>Cached value if it was in cache</returns>
|
/// <returns>Cached value if it was in cache</returns>
|
||||||
public object? Get(string key, TimeSpan maxAge)
|
public object? Get(string key, TimeSpan maxAge)
|
||||||
{
|
{
|
||||||
_cache.TryGetValue(key, out CacheItem value);
|
_cache.TryGetValue(key, out CacheItem? value);
|
||||||
if (value == null)
|
if (value == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OutputOriginalData { get; }
|
public bool OutputOriginalData { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Whether or not API credentials have been configured for this client. Does not check the credentials are actually valid.
|
|
||||||
/// </summary>
|
|
||||||
public bool Authenticated => ApiOptions.ApiCredentials != null || ClientOptions.ApiCredentials != null;
|
public bool Authenticated => ApiOptions.ApiCredentials != null || ClientOptions.ApiCredentials != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,6 +91,17 @@ namespace CryptoExchange.Net.Clients
|
|||||||
AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy());
|
AuthenticationProvider = CreateAuthenticationProvider(credentials.Copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials
|
||||||
|
{
|
||||||
|
ClientOptions.Proxy = options.Proxy;
|
||||||
|
ClientOptions.RequestTimeout = options.RequestTimeout ?? ClientOptions.RequestTimeout;
|
||||||
|
|
||||||
|
ApiOptions.ApiCredentials = options.ApiCredentials ?? ClientOptions.ApiCredentials;
|
||||||
|
if (options.ApiCredentials != null)
|
||||||
|
AuthenticationProvider = CreateAuthenticationProvider(options.ApiCredentials.Copy());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dispose
|
/// Dispose
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the CryptoExchange.Net base library
|
/// Version of the CryptoExchange.Net base library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Version CryptoExchangeLibVersion { get; } = typeof(BaseClient).Assembly.GetName().Version;
|
public Version CryptoExchangeLibVersion { get; } = typeof(BaseClient).Assembly.GetName().Version!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the client implementation
|
/// Version of the client implementation
|
||||||
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
lock(_versionLock)
|
lock(_versionLock)
|
||||||
{
|
{
|
||||||
if (_exchangeVersion == null)
|
if (_exchangeVersion == null)
|
||||||
_exchangeVersion = GetType().Assembly.GetName().Version;
|
_exchangeVersion = GetType().Assembly.GetName().Version!;
|
||||||
|
|
||||||
return _exchangeVersion;
|
return _exchangeVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="additionalHeaders">Additional headers for this request</param>
|
/// <param name="additionalHeaders">Additional headers for this request</param>
|
||||||
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
|
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
|
||||||
|
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task<WebCallResult<T>> SendAsync<T>(
|
protected virtual Task<WebCallResult<T>> SendAsync<T>(
|
||||||
string baseAddress,
|
string baseAddress,
|
||||||
@@ -161,7 +162,8 @@ namespace CryptoExchange.Net.Clients
|
|||||||
ParameterCollection? parameters,
|
ParameterCollection? parameters,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, string>? additionalHeaders = null,
|
Dictionary<string, string>? additionalHeaders = null,
|
||||||
int? weight = null) where T : class
|
int? weight = null,
|
||||||
|
int? weightSingleLimiter = null)
|
||||||
{
|
{
|
||||||
var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
|
var parameterPosition = definition.ParameterPosition ?? ParameterPositions[definition.Method];
|
||||||
return SendAsync<T>(
|
return SendAsync<T>(
|
||||||
@@ -171,7 +173,8 @@ namespace CryptoExchange.Net.Clients
|
|||||||
parameterPosition == HttpMethodParameterPosition.InBody ? parameters : null,
|
parameterPosition == HttpMethodParameterPosition.InBody ? parameters : null,
|
||||||
cancellationToken,
|
cancellationToken,
|
||||||
additionalHeaders,
|
additionalHeaders,
|
||||||
weight);
|
weight,
|
||||||
|
weightSingleLimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -185,6 +188,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="additionalHeaders">Additional headers for this request</param>
|
/// <param name="additionalHeaders">Additional headers for this request</param>
|
||||||
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
|
/// <param name="weight">Override the request weight for this request definition, for example when the weight depends on the parameters</param>
|
||||||
|
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual async Task<WebCallResult<T>> SendAsync<T>(
|
protected virtual async Task<WebCallResult<T>> SendAsync<T>(
|
||||||
string baseAddress,
|
string baseAddress,
|
||||||
@@ -193,7 +197,8 @@ namespace CryptoExchange.Net.Clients
|
|||||||
ParameterCollection? bodyParameters,
|
ParameterCollection? bodyParameters,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, string>? additionalHeaders = null,
|
Dictionary<string, string>? additionalHeaders = null,
|
||||||
int? weight = null) where T : class
|
int? weight = null,
|
||||||
|
int? weightSingleLimiter = null)
|
||||||
{
|
{
|
||||||
string? cacheKey = null;
|
string? cacheKey = null;
|
||||||
if (ShouldCache(definition))
|
if (ShouldCache(definition))
|
||||||
@@ -217,7 +222,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
currentTry++;
|
currentTry++;
|
||||||
var requestId = ExchangeHelpers.NextId();
|
var requestId = ExchangeHelpers.NextId();
|
||||||
|
|
||||||
var prepareResult = await PrepareAsync(requestId, baseAddress, definition, cancellationToken, additionalHeaders, weight).ConfigureAwait(false);
|
var prepareResult = await PrepareAsync(requestId, baseAddress, definition, cancellationToken, additionalHeaders, weight, weightSingleLimiter).ConfigureAwait(false);
|
||||||
if (!prepareResult)
|
if (!prepareResult)
|
||||||
return new WebCallResult<T>(prepareResult.Error!);
|
return new WebCallResult<T>(prepareResult.Error!);
|
||||||
|
|
||||||
@@ -258,6 +263,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="additionalHeaders">Additional headers for this request</param>
|
/// <param name="additionalHeaders">Additional headers for this request</param>
|
||||||
/// <param name="weight">Override the request weight for this request</param>
|
/// <param name="weight">Override the request weight for this request</param>
|
||||||
|
/// <param name="weightSingleLimiter">Specify the weight to apply to the individual rate limit guard for this request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
protected virtual async Task<CallResult> PrepareAsync(
|
protected virtual async Task<CallResult> PrepareAsync(
|
||||||
@@ -266,10 +272,9 @@ namespace CryptoExchange.Net.Clients
|
|||||||
RequestDefinition definition,
|
RequestDefinition definition,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, string>? additionalHeaders = null,
|
Dictionary<string, string>? additionalHeaders = null,
|
||||||
int? weight = null)
|
int? weight = null,
|
||||||
|
int? weightSingleLimiter = null)
|
||||||
{
|
{
|
||||||
var requestWeight = weight ?? definition.Weight;
|
|
||||||
|
|
||||||
// Time sync
|
// Time sync
|
||||||
if (definition.Authenticated)
|
if (definition.Authenticated)
|
||||||
{
|
{
|
||||||
@@ -295,6 +300,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rate limiting
|
// Rate limiting
|
||||||
|
var requestWeight = weight ?? definition.Weight;
|
||||||
if (requestWeight != 0)
|
if (requestWeight != 0)
|
||||||
{
|
{
|
||||||
if (definition.RateLimitGate == null)
|
if (definition.RateLimitGate == null)
|
||||||
@@ -316,7 +322,8 @@ namespace CryptoExchange.Net.Clients
|
|||||||
|
|
||||||
if (ClientOptions.RateLimiterEnabled)
|
if (ClientOptions.RateLimiterEnabled)
|
||||||
{
|
{
|
||||||
var limitResult = await definition.RateLimitGate.ProcessSingleAsync(_logger, requestId, definition.LimitGuard, RateLimitItemType.Request, definition, baseAddress, AuthenticationProvider?._credentials.Key, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
|
var singleRequestWeight = weightSingleLimiter ?? 1;
|
||||||
|
var limitResult = await definition.RateLimitGate.ProcessSingleAsync(_logger, requestId, definition.LimitGuard, RateLimitItemType.Request, definition, baseAddress, AuthenticationProvider?._credentials.Key, singleRequestWeight, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
|
||||||
if (!limitResult)
|
if (!limitResult)
|
||||||
return new CallResult(limitResult.Error!);
|
return new CallResult(limitResult.Error!);
|
||||||
}
|
}
|
||||||
@@ -617,7 +624,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
paramString = $" with request body '{request.Content}'";
|
paramString = $" with request body '{request.Content}'";
|
||||||
|
|
||||||
var headers = request.GetHeaders();
|
var headers = request.GetHeaders();
|
||||||
if (headers.Any())
|
if (headers.Count != 0)
|
||||||
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)}]"));
|
||||||
|
|
||||||
TotalRequestsMade++;
|
TotalRequestsMade++;
|
||||||
@@ -693,10 +700,21 @@ namespace CryptoExchange.Net.Clients
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Json response received
|
// Json response received
|
||||||
var parsedError = TryParseError(accessor);
|
var parsedError = TryParseError(response.ResponseHeaders, accessor);
|
||||||
if (parsedError != null)
|
if (parsedError != null)
|
||||||
|
{
|
||||||
|
if (parsedError is ServerRateLimitError rateError)
|
||||||
|
{
|
||||||
|
if (rateError.RetryAfter != null && gate != null && ClientOptions.RateLimiterEnabled)
|
||||||
|
{
|
||||||
|
_logger.RestApiRateLimitPauseUntil(request.RequestId, rateError.RetryAfter.Value);
|
||||||
|
await gate.SetRetryAfterGuardAsync(rateError.RetryAfter.Value).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Success status code, but TryParseError determined it was an error response
|
// Success status code, but TryParseError determined it was an error response
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
||||||
|
}
|
||||||
|
|
||||||
var deserializeResult = accessor.Deserialize<T>();
|
var deserializeResult = accessor.Deserialize<T>();
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
|
||||||
@@ -730,12 +748,13 @@ namespace CryptoExchange.Net.Clients
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can be used to parse an error even though response status indicates success. Some apis always return 200 OK, even though there is an error.
|
/// Can be used to parse an error even though response status indicates success. Some apis always return 200 OK, even though there is an error.
|
||||||
/// When setting manualParseError to true this method will be called for each response to be able to check if the response is an error or not.
|
/// This method will be called for each response to be able to check if the response is an error or not.
|
||||||
/// If the response is an error this method should return the parsed error, else it should return null
|
/// If the response is an error this method should return the parsed error, else it should return null
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="accessor">Data accessor</param>
|
/// <param name="accessor">Data accessor</param>
|
||||||
|
/// <param name="responseHeaders">The response headers</param>
|
||||||
/// <returns>Null if not an error, Error otherwise</returns>
|
/// <returns>Null if not an error, Error otherwise</returns>
|
||||||
protected virtual ServerError? TryParseError(IMessageAccessor accessor) => null;
|
protected virtual Error? TryParseError(IEnumerable<KeyValuePair<string, IEnumerable<string>>> responseHeaders, IMessageAccessor accessor) => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can be used to indicate that a request should be retried. Defaults to false. Make sure to retry a max number of times (based on the the tries parameter) or the request will retry forever.
|
/// Can be used to indicate that a request should be retried. Defaults to false. Make sure to retry a max number of times (based on the the tries parameter) or the request will retry forever.
|
||||||
@@ -752,7 +771,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
// Only retry once
|
// Only retry once
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((int?)callResult.ResponseStatusCode == 429
|
if (callResult.Error is ServerRateLimitError
|
||||||
&& ClientOptions.RateLimiterEnabled
|
&& ClientOptions.RateLimiterEnabled
|
||||||
&& ClientOptions.RateLimitingBehaviour != RateLimitingBehaviour.Fail
|
&& ClientOptions.RateLimitingBehaviour != RateLimitingBehaviour.Fail
|
||||||
&& gate != null)
|
&& gate != null)
|
||||||
@@ -807,7 +826,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
if (parameterPosition == HttpMethodParameterPosition.InUri)
|
if (parameterPosition == HttpMethodParameterPosition.InUri)
|
||||||
{
|
{
|
||||||
foreach (var parameter in parameters)
|
foreach (var parameter in parameters)
|
||||||
uri = uri.AddQueryParmeter(parameter.Key, parameter.Value.ToString());
|
uri = uri.AddQueryParmeter(parameter.Key, parameter.Value.ToString()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
@@ -889,8 +908,8 @@ namespace CryptoExchange.Net.Clients
|
|||||||
{
|
{
|
||||||
// Write the parameters as json in the body
|
// Write the parameters as json in the body
|
||||||
string stringData;
|
string stringData;
|
||||||
if (parameters.Count == 1 && parameters.ContainsKey(Constants.BodyPlaceHolderKey))
|
if (parameters.Count == 1 && parameters.TryGetValue(Constants.BodyPlaceHolderKey, out object? value))
|
||||||
stringData = CreateSerializer().Serialize(parameters[Constants.BodyPlaceHolderKey]);
|
stringData = CreateSerializer().Serialize(value);
|
||||||
else
|
else
|
||||||
stringData = CreateSerializer().Serialize(parameters);
|
stringData = CreateSerializer().Serialize(parameters);
|
||||||
request.SetContent(stringData, contentType);
|
request.SetContent(stringData, contentType);
|
||||||
@@ -961,6 +980,14 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <returns>Server time</returns>
|
/// <returns>Server time</returns>
|
||||||
protected virtual Task<WebCallResult<DateTime>> GetServerTimestampAsync() => throw new NotImplementedException();
|
protected virtual Task<WebCallResult<DateTime>> GetServerTimestampAsync() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void SetOptions<T>(UpdateOptions<T> options)
|
||||||
|
{
|
||||||
|
base.SetOptions(options);
|
||||||
|
|
||||||
|
RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task<WebCallResult<bool>> SyncTimeAsync()
|
internal async Task<WebCallResult<bool>> SyncTimeAsync()
|
||||||
{
|
{
|
||||||
var timeSyncParams = GetTimeSyncInfo();
|
var timeSyncParams = GetTimeSyncInfo();
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// <param name="interval"></param>
|
/// <param name="interval"></param>
|
||||||
/// <param name="queryDelegate"></param>
|
/// <param name="queryDelegate"></param>
|
||||||
/// <param name="callback"></param>
|
/// <param name="callback"></param>
|
||||||
protected virtual void RegisterPeriodicQuery(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<CallResult>? callback)
|
protected virtual void RegisterPeriodicQuery(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<SocketConnection, CallResult>? callback)
|
||||||
{
|
{
|
||||||
PeriodicTaskRegistrations.Add(new PeriodicTaskRegistration
|
PeriodicTaskRegistrations.Add(new PeriodicTaskRegistration
|
||||||
{
|
{
|
||||||
@@ -422,9 +422,10 @@ namespace CryptoExchange.Net.Clients
|
|||||||
result.Error!.Message = "Authentication failed: " + result.Error.Message;
|
result.Error!.Message = "Authentication failed: " + result.Error.Message;
|
||||||
return new CallResult(result.Error)!;
|
return new CallResult(result.Error)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.Authenticated(socket.SocketId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Authenticated(socket.SocketId);
|
|
||||||
socket.Authenticated = true;
|
socket.Authenticated = true;
|
||||||
return new CallResult(null);
|
return new CallResult(null);
|
||||||
}
|
}
|
||||||
@@ -710,6 +711,25 @@ namespace CryptoExchange.Net.Clients
|
|||||||
return new CallResult(null);
|
return new CallResult(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void SetOptions<T>(UpdateOptions<T> options)
|
||||||
|
{
|
||||||
|
var previousProxyIsSet = ClientOptions.Proxy != null;
|
||||||
|
base.SetOptions(options);
|
||||||
|
|
||||||
|
if ((!previousProxyIsSet && options.Proxy == null)
|
||||||
|
|| !socketConnections.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Reconnecting websockets to apply proxy");
|
||||||
|
|
||||||
|
// Update proxy, also triggers reconnect
|
||||||
|
foreach (var connection in socketConnections)
|
||||||
|
_ = connection.Value.UpdateProxy(options.Proxy);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Log the current state of connections and subscriptions
|
/// Log the current state of connections and subscriptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
|
|
||||||
var result = Activator.CreateInstance(objectType);
|
var result = Activator.CreateInstance(objectType);
|
||||||
var arr = JArray.Load(reader);
|
var arr = JArray.Load(reader);
|
||||||
return ParseObject(arr, result, objectType);
|
return ParseObject(arr, result!, objectType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object ParseObject(JArray arr, object result, Type objectType)
|
private static object ParseObject(JArray arr, object result, Type objectType)
|
||||||
@@ -58,25 +58,25 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
var count = 0;
|
var count = 0;
|
||||||
if (innerArray.Count == 0)
|
if (innerArray.Count == 0)
|
||||||
{
|
{
|
||||||
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { 0 });
|
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { 0 })!;
|
||||||
property.SetValue(result, arrayResult);
|
property.SetValue(result, arrayResult);
|
||||||
}
|
}
|
||||||
else if (innerArray[0].Type == JTokenType.Array)
|
else if (innerArray[0].Type == JTokenType.Array)
|
||||||
{
|
{
|
||||||
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { innerArray.Count });
|
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { innerArray.Count })!;
|
||||||
foreach (var obj in innerArray)
|
foreach (var obj in innerArray)
|
||||||
{
|
{
|
||||||
var innerObj = Activator.CreateInstance(objType!);
|
var innerObj = Activator.CreateInstance(objType!);
|
||||||
arrayResult[count] = ParseObject((JArray)obj, innerObj, objType!);
|
arrayResult[count] = ParseObject((JArray)obj, innerObj!, objType!);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
property.SetValue(result, arrayResult);
|
property.SetValue(result, arrayResult);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { 1 });
|
var arrayResult = (IList)Activator.CreateInstance(property.PropertyType, new [] { 1 })!;
|
||||||
var innerObj = Activator.CreateInstance(objType!);
|
var innerObj = Activator.CreateInstance(objType!);
|
||||||
arrayResult[0] = ParseObject(innerArray, innerObj, objType!);
|
arrayResult[0] = ParseObject(innerArray, innerObj!, objType!);
|
||||||
property.SetValue(result, arrayResult);
|
property.SetValue(result, arrayResult);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -88,7 +88,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
object? value;
|
object? value;
|
||||||
if (converterAttribute != null)
|
if (converterAttribute != null)
|
||||||
{
|
{
|
||||||
value = arr[attribute.Index].ToObject(property.PropertyType, new JsonSerializer {Converters = {(JsonConverter) Activator.CreateInstance(converterAttribute.ConverterType)}});
|
value = arr[attribute.Index].ToObject(property.PropertyType, new JsonSerializer {Converters = {(JsonConverter) Activator.CreateInstance(converterAttribute.ConverterType)!}});
|
||||||
}
|
}
|
||||||
else if (conversionAttribute != null)
|
else if (conversionAttribute != null)
|
||||||
{
|
{
|
||||||
@@ -120,7 +120,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
}
|
}
|
||||||
else if ((property.PropertyType == typeof(decimal)
|
else if ((property.PropertyType == typeof(decimal)
|
||||||
|| property.PropertyType == typeof(decimal?))
|
|| property.PropertyType == typeof(decimal?))
|
||||||
&& (value != null && value.ToString().IndexOf("e", StringComparison.OrdinalIgnoreCase) >= 0))
|
&& (value != null && value.ToString()!.IndexOf("e", StringComparison.OrdinalIgnoreCase) >= 0))
|
||||||
{
|
{
|
||||||
var v = value.ToString();
|
var v = value.ToString();
|
||||||
if (decimal.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out var dec))
|
if (decimal.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out var dec))
|
||||||
@@ -164,7 +164,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
last = arrayProp.Index;
|
last = arrayProp.Index;
|
||||||
var converterAttribute = GetCustomAttribute<JsonConverterAttribute>(prop);
|
var converterAttribute = GetCustomAttribute<JsonConverterAttribute>(prop);
|
||||||
if (converterAttribute != null)
|
if (converterAttribute != null)
|
||||||
writer.WriteRawValue(JsonConvert.SerializeObject(prop.GetValue(value), (JsonConverter)Activator.CreateInstance(converterAttribute.ConverterType)));
|
writer.WriteRawValue(JsonConvert.SerializeObject(prop.GetValue(value), (JsonConverter)Activator.CreateInstance(converterAttribute.ConverterType)!));
|
||||||
else if (!IsSimple(prop.PropertyType))
|
else if (!IsSimple(prop.PropertyType))
|
||||||
serializer.Serialize(writer, prop.GetValue(value));
|
serializer.Serialize(writer, prop.GetValue(value));
|
||||||
else
|
else
|
||||||
@@ -187,9 +187,9 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static T? GetCustomAttribute<T>(MemberInfo memberInfo) where T : Attribute =>
|
private static T? GetCustomAttribute<T>(MemberInfo memberInfo) where T : Attribute =>
|
||||||
(T?)_attributeByMemberInfoAndTypeCache.GetOrAdd((memberInfo, typeof(T)), tuple => memberInfo.GetCustomAttribute(typeof(T)));
|
(T?)_attributeByMemberInfoAndTypeCache.GetOrAdd((memberInfo, typeof(T)), tuple => memberInfo.GetCustomAttribute(typeof(T))!);
|
||||||
|
|
||||||
private static T? GetCustomAttribute<T>(Type type) where T : Attribute =>
|
private static T? GetCustomAttribute<T>(Type type) where T : Attribute =>
|
||||||
(T?)_attributeByTypeAndTypeCache.GetOrAdd((type, typeof(T)), tuple => type.GetCustomAttribute(typeof(T)));
|
(T?)_attributeByTypeAndTypeCache.GetOrAdd((type, typeof(T)), tuple => type.GetCustomAttribute(typeof(T))!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return decimal.Parse(reader.Value!.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture);
|
return decimal.Parse(reader.Value!.ToString()!, NumberStyles.Float, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
catch (OverflowException)
|
catch (OverflowException)
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var value = reader.Value!.ToString();
|
var value = reader.Value!.ToString()!;
|
||||||
return decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture);
|
return decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
catch (OverflowException)
|
catch (OverflowException)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
/// </returns>
|
/// </returns>
|
||||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
var value = reader.Value?.ToString().ToLower().Trim();
|
var value = reader.Value?.ToString()!.ToLower().Trim();
|
||||||
if (value == null || value == "")
|
if (value == null || value == "")
|
||||||
{
|
{
|
||||||
if (Nullable.GetUnderlyingType(objectType) != null)
|
if (Nullable.GetUnderlyingType(objectType) != null)
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ namespace CryptoExchange.Net.Converters.JsonNet
|
|||||||
|
|
||||||
// Try getting the underlying byte[] instead of the ToArray to prevent creating a copy
|
// Try getting the underlying byte[] instead of the ToArray to prevent creating a copy
|
||||||
using var stream = MemoryMarshal.TryGetArray(data, out var arraySegment)
|
using var stream = MemoryMarshal.TryGetArray(data, out var arraySegment)
|
||||||
? new MemoryStream(arraySegment.Array, arraySegment.Offset, arraySegment.Count)
|
? new MemoryStream(arraySegment.Array!, arraySegment.Offset, arraySegment.Count)
|
||||||
: new MemoryStream(data.ToArray());
|
: new MemoryStream(data.ToArray());
|
||||||
using var reader = new StreamReader(stream, Encoding.UTF8, false, Math.Max(2, data.Length), true);
|
using var reader = new StreamReader(stream, Encoding.UTF8, false, Math.Max(2, data.Length), true);
|
||||||
using var jsonTextReader = new JsonTextReader(reader);
|
using var jsonTextReader = new JsonTextReader(reader);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
Type converterType = typeof(ArrayConverterInner<>).MakeGenericType(typeToConvert);
|
Type converterType = typeof(ArrayConverterInner<>).MakeGenericType(typeToConvert);
|
||||||
return (JsonConverter)Activator.CreateInstance(converterType);
|
return (JsonConverter)Activator.CreateInstance(converterType)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArrayPropertyInfo
|
private class ArrayPropertyInfo
|
||||||
@@ -79,7 +79,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
JsonSerializerOptions? typeOptions = null;
|
JsonSerializerOptions? typeOptions = null;
|
||||||
if (prop.JsonConverterType != null)
|
if (prop.JsonConverterType != null)
|
||||||
{
|
{
|
||||||
var converter = (JsonConverter)Activator.CreateInstance(prop.JsonConverterType);
|
var converter = (JsonConverter)Activator.CreateInstance(prop.JsonConverterType)!;
|
||||||
typeOptions = new JsonSerializerOptions();
|
typeOptions = new JsonSerializerOptions();
|
||||||
typeOptions.Converters.Clear();
|
typeOptions.Converters.Clear();
|
||||||
typeOptions.Converters.Add(converter);
|
typeOptions.Converters.Add(converter);
|
||||||
@@ -89,8 +89,12 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
if (prop.PropertyInfo.PropertyType == typeof(string))
|
if (prop.PropertyInfo.PropertyType == typeof(string))
|
||||||
writer.WriteStringValue(Convert.ToString(objValue, CultureInfo.InvariantCulture));
|
writer.WriteStringValue(Convert.ToString(objValue, CultureInfo.InvariantCulture));
|
||||||
|
else if(prop.PropertyInfo.PropertyType.IsEnum)
|
||||||
|
writer.WriteStringValue(EnumConverter.GetString(objValue));
|
||||||
|
else if (prop.PropertyInfo.PropertyType == typeof(bool))
|
||||||
|
writer.WriteBooleanValue((bool)objValue);
|
||||||
else
|
else
|
||||||
writer.WriteRawValue(Convert.ToString(objValue, CultureInfo.InvariantCulture));
|
writer.WriteRawValue(Convert.ToString(objValue, CultureInfo.InvariantCulture)!);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -107,7 +111,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
if (reader.TokenType == JsonTokenType.Null)
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var result = Activator.CreateInstance(typeToConvert);
|
var result = Activator.CreateInstance(typeToConvert)!;
|
||||||
return (T)ParseObject(ref reader, result, typeToConvert, options);
|
return (T)ParseObject(ref reader, result, typeToConvert, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +181,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
if (!_converterOptionsCache.TryGetValue(attribute.JsonConverterType, out var newOptions))
|
if (!_converterOptionsCache.TryGetValue(attribute.JsonConverterType, out var newOptions))
|
||||||
{
|
{
|
||||||
var converter = (JsonConverter)Activator.CreateInstance(attribute.JsonConverterType);
|
var converter = (JsonConverter)Activator.CreateInstance(attribute.JsonConverterType)!;
|
||||||
newOptions = new JsonSerializerOptions
|
newOptions = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
NumberHandling = SerializerOptions.WithConverters.NumberHandling,
|
NumberHandling = SerializerOptions.WithConverters.NumberHandling,
|
||||||
@@ -187,12 +191,12 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
_converterOptionsCache.TryAdd(attribute.JsonConverterType, newOptions);
|
_converterOptionsCache.TryAdd(attribute.JsonConverterType, newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = JsonDocument.ParseValue(ref reader).Deserialize(targetType, newOptions);
|
value = JsonDocument.ParseValue(ref reader).Deserialize(attribute.PropertyInfo.PropertyType, newOptions);
|
||||||
}
|
}
|
||||||
else if (attribute.DefaultDeserialization)
|
else if (attribute.DefaultDeserialization)
|
||||||
{
|
{
|
||||||
// Use default deserialization
|
// Use default deserialization
|
||||||
value = JsonDocument.ParseValue(ref reader).Deserialize(targetType, options);
|
value = JsonDocument.ParseValue(ref reader).Deserialize(attribute.PropertyInfo.PropertyType, SerializerOptions.WithConverters);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,7 +213,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (targetType.IsAssignableFrom(value?.GetType()))
|
if (targetType.IsAssignableFrom(value?.GetType()))
|
||||||
attribute.PropertyInfo.SetValue(result, value == null ? null : value);
|
attribute.PropertyInfo.SetValue(result, value);
|
||||||
else
|
else
|
||||||
attribute.PropertyInfo.SetValue(result, value == null ? null : Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture));
|
attribute.PropertyInfo.SetValue(result, value == null ? null : Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return decimal.Parse(reader.GetString(), NumberStyles.Float, CultureInfo.InvariantCulture);
|
return decimal.Parse(reader.GetString()!, NumberStyles.Float, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
catch(OverflowException)
|
catch(OverflowException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
Type converterType = typeof(BoolConverterInner<>).MakeGenericType(typeToConvert);
|
Type converterType = typeof(BoolConverterInner<>).MakeGenericType(typeToConvert);
|
||||||
return (JsonConverter)Activator.CreateInstance(converterType);
|
return (JsonConverter)Activator.CreateInstance(converterType)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BoolConverterInner<T> : JsonConverter<T>
|
private class BoolConverterInner<T> : JsonConverter<T>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
Type converterType = typeof(DateTimeConverterInner<>).MakeGenericType(typeToConvert);
|
Type converterType = typeof(DateTimeConverterInner<>).MakeGenericType(typeToConvert);
|
||||||
return (JsonConverter)Activator.CreateInstance(converterType);
|
return (JsonConverter)Activator.CreateInstance(converterType)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DateTimeConverterInner<T> : JsonConverter<T>
|
private class DateTimeConverterInner<T> : JsonConverter<T>
|
||||||
@@ -74,7 +74,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
|
{
|
||||||
writer.WriteNullValue();
|
writer.WriteNullValue();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var dtValue = (DateTime)(object)value;
|
var dtValue = (DateTime)(object)value;
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.SystemTextJson
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter mapping to an object but also handles when an empty array is send
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public class EmptyArrayObjectConverter<T> : JsonConverter<T>
|
||||||
|
{
|
||||||
|
private static JsonSerializerOptions _defaultConverter = SerializerOptions.WithConverters;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override T? Read(
|
||||||
|
ref Utf8JsonReader reader,
|
||||||
|
Type typeToConvert,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
switch (reader.TokenType)
|
||||||
|
{
|
||||||
|
case JsonTokenType.StartArray:
|
||||||
|
_ = JsonSerializer.Deserialize<object[]>(ref reader, options);
|
||||||
|
return default;
|
||||||
|
case JsonTokenType.StartObject:
|
||||||
|
return JsonSerializer.Deserialize<T>(ref reader, _defaultConverter);
|
||||||
|
};
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||||
|
=> JsonSerializer.Serialize(writer, (object?)value, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -172,6 +172,13 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (objectType.IsDefined(typeof(FlagsAttribute)))
|
||||||
|
{
|
||||||
|
var intValue = int.Parse(value);
|
||||||
|
result = Enum.ToObject(objectType, intValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// If no explicit mapping is found try to parse string
|
// If no explicit mapping is found try to parse string
|
||||||
|
|||||||
@@ -6,21 +6,25 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attribute for allowing specifying a JsonConverter with constructor parameters
|
/// Attribute for allowing specifying a JsonConverter with constructor parameters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class JsonConverterCtorAttribute<T> : JsonConverterAttribute where T : JsonConverter
|
public class JsonConverterCtorAttribute : JsonConverterAttribute
|
||||||
{
|
{
|
||||||
private readonly object[] _parameters;
|
private readonly object[] _parameters;
|
||||||
|
private readonly Type _type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JsonConverterCtorAttribute(params object[] parameters) => _parameters = parameters;
|
public JsonConverterCtorAttribute(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
_type = type;
|
||||||
|
_parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override JsonConverter CreateConverter(Type typeToConvert)
|
public override JsonConverter CreateConverter(Type typeToConvert)
|
||||||
{
|
{
|
||||||
return (T)Activator.CreateInstance(typeof(T), _parameters);
|
return (JsonConverter)Activator.CreateInstance(_type, _parameters)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
protected JsonDocument? _document;
|
protected JsonDocument? _document;
|
||||||
|
|
||||||
private static JsonSerializerOptions _serializerOptions = SerializerOptions.WithConverters;
|
private static JsonSerializerOptions _serializerOptions = SerializerOptions.WithConverters;
|
||||||
|
private JsonSerializerOptions? _customSerializerOptions;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool IsJson { get; set; }
|
public bool IsJson { get; set; }
|
||||||
@@ -31,6 +32,21 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object? Underlying => throw new NotImplementedException();
|
public object? Underlying => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonMessageAccessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonMessageAccessor(JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
_customSerializerOptions = options;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CallResult<object> Deserialize(Type type, MessagePath? path = null)
|
public CallResult<object> Deserialize(Type type, MessagePath? path = null)
|
||||||
{
|
{
|
||||||
@@ -42,7 +58,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = _document.Deserialize(type, _serializerOptions);
|
var result = _document.Deserialize(type, _customSerializerOptions ?? _serializerOptions);
|
||||||
return new CallResult<object>(result!);
|
return new CallResult<object>(result!);
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException ex)
|
||||||
@@ -65,7 +81,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = _document.Deserialize<T>(_serializerOptions);
|
var result = _document.Deserialize<T>(_customSerializerOptions ?? _serializerOptions);
|
||||||
return new CallResult<T>(result!);
|
return new CallResult<T>(result!);
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException ex)
|
||||||
@@ -129,7 +145,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return value.Value.Deserialize<T>(_serializerOptions);
|
return value.Value.Deserialize<T>(_customSerializerOptions ?? _serializerOptions);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
return default;
|
return default;
|
||||||
@@ -223,6 +239,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OriginalDataAvailable => _stream?.CanSeek == true;
|
public override bool OriginalDataAvailable => _stream?.CanSeek == true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonStreamMessageAccessor(): base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonStreamMessageAccessor(JsonSerializerOptions options): base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<CallResult> Read(Stream stream, bool bufferStream)
|
public async Task<CallResult> Read(Stream stream, bool bufferStream)
|
||||||
{
|
{
|
||||||
@@ -286,6 +316,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
private ReadOnlyMemory<byte> _bytes;
|
private ReadOnlyMemory<byte> _bytes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonByteMessageAccessor() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public SystemTextJsonByteMessageAccessor(JsonSerializerOptions options) : base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CallResult Read(ReadOnlyMemory<byte> data)
|
public CallResult Read(ReadOnlyMemory<byte> data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net9.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
||||||
<PackageVersion>8.4.3</PackageVersion>
|
<PackageVersion>8.7.2</PackageVersion>
|
||||||
<AssemblyVersion>8.4.3</AssemblyVersion>
|
<AssemblyVersion>8.7.2</AssemblyVersion>
|
||||||
<FileVersion>8.4.3</FileVersion>
|
<FileVersion>8.7.2</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange</PackageTags>
|
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
var randomChars = new char[length];
|
var randomChars = new char[length];
|
||||||
|
|
||||||
#if NETSTANDARD2_1_OR_GREATER
|
#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
randomChars[i] = _allowedRandomChars[RandomNumberGenerator.GetInt32(0, _allowedRandomChars.Length)];
|
randomChars[i] = _allowedRandomChars[RandomNumberGenerator.GetInt32(0, _allowedRandomChars.Length)];
|
||||||
#else
|
#else
|
||||||
@@ -261,6 +261,7 @@ namespace CryptoExchange.Net
|
|||||||
if (price != null)
|
if (price != null)
|
||||||
{
|
{
|
||||||
adjustedPrice = AdjustValueStep(0, decimal.MaxValue, symbol.PriceStep, RoundingType.Down, price.Value);
|
adjustedPrice = AdjustValueStep(0, decimal.MaxValue, symbol.PriceStep, RoundingType.Down, price.Value);
|
||||||
|
adjustedPrice = symbol.PriceSignificantFigures.HasValue ? RoundToSignificantDigits(adjustedPrice.Value, symbol.PriceSignificantFigures.Value, RoundingType.Closest) : adjustedPrice;
|
||||||
adjustedPrice = symbol.PriceDecimals.HasValue ? RoundDown(price.Value, symbol.PriceDecimals.Value) : adjustedPrice;
|
adjustedPrice = symbol.PriceDecimals.HasValue ? RoundDown(price.Value, symbol.PriceDecimals.Value) : adjustedPrice;
|
||||||
if (adjustedPrice != 0 && adjustedPrice * quantity < symbol.MinNotionalValue)
|
if (adjustedPrice != 0 && adjustedPrice * quantity < symbol.MinNotionalValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
if (serializationType == ArrayParametersSerialization.Array)
|
if (serializationType == ArrayParametersSerialization.Array)
|
||||||
{
|
{
|
||||||
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={string.Format(CultureInfo.InvariantCulture, "{0}", v)}"))}&";
|
uriString += $"{string.Join("&", ((object[])(urlEncodeValues ? Uri.EscapeDataString(arrayEntry.Value.ToString()!) : arrayEntry.Value)).Select(v => $"{arrayEntry.Key}[]={string.Format(CultureInfo.InvariantCulture, "{0}", v)}"))}&";
|
||||||
}
|
}
|
||||||
else if (serializationType == ArrayParametersSerialization.MultipleValues)
|
else if (serializationType == ArrayParametersSerialization.MultipleValues)
|
||||||
{
|
{
|
||||||
@@ -111,7 +111,7 @@ namespace CryptoExchange.Net
|
|||||||
formData.Add(kvp.Key, string.Format(CultureInfo.InvariantCulture, "{0}", kvp.Value));
|
formData.Add(kvp.Key, string.Format(CultureInfo.InvariantCulture, "{0}", kvp.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return formData.ToString();
|
return formData.ToString()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -366,7 +366,7 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
using var decompressedStream = new MemoryStream();
|
using var decompressedStream = new MemoryStream();
|
||||||
using var dataStream = MemoryMarshal.TryGetArray(data, out var arraySegment)
|
using var dataStream = MemoryMarshal.TryGetArray(data, out var arraySegment)
|
||||||
? new MemoryStream(arraySegment.Array, arraySegment.Offset, arraySegment.Count)
|
? new MemoryStream(arraySegment.Array!, arraySegment.Offset, arraySegment.Count)
|
||||||
: new MemoryStream(data.ToArray());
|
: new MemoryStream(data.ToArray());
|
||||||
using var deflateStream = new GZipStream(new MemoryStream(data.ToArray()), CompressionMode.Decompress);
|
using var deflateStream = new GZipStream(new MemoryStream(data.ToArray()), CompressionMode.Decompress);
|
||||||
deflateStream.CopyTo(decompressedStream);
|
deflateStream.CopyTo(decompressedStream);
|
||||||
@@ -435,6 +435,8 @@ namespace CryptoExchange.Net
|
|||||||
services.AddTransient(x => (IWithdrawalRestClient)client(x)!);
|
services.AddTransient(x => (IWithdrawalRestClient)client(x)!);
|
||||||
if (typeof(IWithdrawRestClient).IsAssignableFrom(typeof(T)))
|
if (typeof(IWithdrawRestClient).IsAssignableFrom(typeof(T)))
|
||||||
services.AddTransient(x => (IWithdrawRestClient)client(x)!);
|
services.AddTransient(x => (IWithdrawRestClient)client(x)!);
|
||||||
|
if (typeof(IFeeRestClient).IsAssignableFrom(typeof(T)))
|
||||||
|
services.AddTransient(x => (IFeeRestClient)client(x)!);
|
||||||
|
|
||||||
if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T)))
|
if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T)))
|
||||||
services.AddTransient(x => (ISpotOrderRestClient)client(x)!);
|
services.AddTransient(x => (ISpotOrderRestClient)client(x)!);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using CryptoExchange.Net.Authentication;
|
using CryptoExchange.Net.Authentication;
|
||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Objects.Options;
|
||||||
using CryptoExchange.Net.SharedApis;
|
using CryptoExchange.Net.SharedApis;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -15,6 +16,11 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string BaseAddress { get; }
|
string BaseAddress { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not API credentials have been configured for this client. Does not check the credentials are actually valid.
|
||||||
|
/// </summary>
|
||||||
|
bool Authenticated { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Format a base and quote asset to an exchange accepted symbol
|
/// Format a base and quote asset to an exchange accepted symbol
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -31,5 +37,12 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="credentials"></param>
|
/// <param name="credentials"></param>
|
||||||
void SetApiCredentials<T>(T credentials) where T : ApiCredentials;
|
void SetApiCredentials<T>(T credentials) where T : ApiCredentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set new options. Note that when using a proxy this should be provided in the options even when already set before or it will be reset.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Api credentials type</typeparam>
|
||||||
|
/// <param name="options">Options to set</param>
|
||||||
|
void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,13 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <param name="requestTimeout">Request timeout to use</param>
|
/// <param name="requestTimeout">Request timeout to use</param>
|
||||||
/// <param name="httpClient">Optional shared http client instance</param>
|
/// <param name="httpClient">Optional shared http client instance</param>
|
||||||
/// <param name="proxy">Optional proxy to use when no http client is provided</param>
|
/// <param name="proxy">Optional proxy to use when no http client is provided</param>
|
||||||
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient=null);
|
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update settings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">Proxy to use</param>
|
||||||
|
/// <param name="requestTimeout">Request timeout to use</param>
|
||||||
|
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,5 +93,10 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task CloseAsync();
|
Task CloseAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update proxy setting
|
||||||
|
/// </summary>
|
||||||
|
void UpdateProxy(ApiProxy? proxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-2
@@ -35,6 +35,7 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
private static readonly Action<ILogger, int, TimeSpan?, Exception?> _startingTaskForNoDataReceivedCheck;
|
private static readonly Action<ILogger, int, TimeSpan?, Exception?> _startingTaskForNoDataReceivedCheck;
|
||||||
private static readonly Action<ILogger, int, TimeSpan?, Exception?> _noDataReceiveTimoutReconnect;
|
private static readonly Action<ILogger, int, TimeSpan?, Exception?> _noDataReceiveTimoutReconnect;
|
||||||
private static readonly Action<ILogger, int, string, string, Exception?> _socketProcessingStateChanged;
|
private static readonly Action<ILogger, int, string, string, Exception?> _socketProcessingStateChanged;
|
||||||
|
private static readonly Action<ILogger, int, Exception?> _socketPingTimeout;
|
||||||
|
|
||||||
static CryptoExchangeWebSocketClientLoggingExtension()
|
static CryptoExchangeWebSocketClientLoggingExtension()
|
||||||
{
|
{
|
||||||
@@ -169,7 +170,7 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
"[Sckt {SocketId}] starting task checking for no data received for {Timeout}");
|
"[Sckt {SocketId}] starting task checking for no data received for {Timeout}");
|
||||||
|
|
||||||
_noDataReceiveTimoutReconnect = LoggerMessage.Define<int, TimeSpan?>(
|
_noDataReceiveTimoutReconnect = LoggerMessage.Define<int, TimeSpan?>(
|
||||||
LogLevel.Debug,
|
LogLevel.Warning,
|
||||||
new EventId(1027, "NoDataReceiveTimeoutReconnect"),
|
new EventId(1027, "NoDataReceiveTimeoutReconnect"),
|
||||||
"[Sckt {SocketId}] no data received for {Timeout}, reconnecting socket");
|
"[Sckt {SocketId}] no data received for {Timeout}, reconnecting socket");
|
||||||
|
|
||||||
@@ -180,9 +181,14 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
|
|
||||||
_socketProcessingStateChanged = LoggerMessage.Define<int, string, string>(
|
_socketProcessingStateChanged = LoggerMessage.Define<int, string, string>(
|
||||||
LogLevel.Trace,
|
LogLevel.Trace,
|
||||||
new EventId(1028, "SocketProcessingStateChanged"),
|
new EventId(1029, "SocketProcessingStateChanged"),
|
||||||
"[Sckt {Id}] processing state change: {PreviousState} -> {NewState}");
|
"[Sckt {Id}] processing state change: {PreviousState} -> {NewState}");
|
||||||
|
|
||||||
|
_socketPingTimeout = LoggerMessage.Define<int>(
|
||||||
|
LogLevel.Warning,
|
||||||
|
new EventId(1030, "SocketPingTimeout"),
|
||||||
|
"[Sckt {Id}] ping frame timeout; reconnecting socket");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SocketConnecting(
|
public static void SocketConnecting(
|
||||||
@@ -358,5 +364,11 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
{
|
{
|
||||||
_socketProcessingStateChanged(logger, socketId, prevState, newState, null);
|
_socketProcessingStateChanged(logger, socketId, prevState, newState, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SocketPingTimeout(
|
||||||
|
this ILogger logger, int socketId)
|
||||||
|
{
|
||||||
|
_socketPingTimeout(logger, socketId, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
_sendingData = LoggerMessage.Define<int, int, string>(
|
_sendingData = LoggerMessage.Define<int, int, string>(
|
||||||
LogLevel.Trace,
|
LogLevel.Trace,
|
||||||
new EventId(2028, "SendingData"),
|
new EventId(2028, "SendingData"),
|
||||||
"[Sckt {SocketId}] [Req {RequestId}] sending messsage: {Data}");
|
"[Sckt {SocketId}] [Req {RequestId}] sending message: {Data}");
|
||||||
|
|
||||||
_receivedMessageNotMatchedToAnyListener = LoggerMessage.Define<int, string, string>(
|
_receivedMessageNotMatchedToAnyListener = LoggerMessage.Define<int, string, string>(
|
||||||
LogLevel.Warning,
|
LogLevel.Warning,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public int Compare(byte[] x, byte[] y)
|
public int Compare(byte[]? x, byte[]? y)
|
||||||
{
|
{
|
||||||
// Shortcuts: If both are null, they are the same.
|
// Shortcuts: If both are null, they are the same.
|
||||||
if (x == null && y == null) return 0;
|
if (x == null && y == null) return 0;
|
||||||
|
|||||||
@@ -235,4 +235,18 @@
|
|||||||
Cache
|
Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of exchange
|
||||||
|
/// </summary>
|
||||||
|
public enum ExchangeType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized
|
||||||
|
/// </summary>
|
||||||
|
CEX,
|
||||||
|
/// <summary>
|
||||||
|
/// Decentralized
|
||||||
|
/// </summary>
|
||||||
|
DEX
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Objects.Options
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Options to update
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateOptions<T> where T : ApiCredentials
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Proxy setting. Note that if this is not provided any previously set proxy will be reset
|
||||||
|
/// </summary>
|
||||||
|
public ApiProxy? Proxy { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Api credentials
|
||||||
|
/// </summary>
|
||||||
|
public T? ApiCredentials { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Request timeout
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? RequestTimeout { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class UpdateOptions : UpdateOptions<ApiCredentials> { }
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="x"></param>
|
/// <param name="x"></param>
|
||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public int Compare(string x, string y)
|
public int Compare(string? x, string? y)
|
||||||
{
|
{
|
||||||
// Shortcuts: If both are null, they are the same.
|
// Shortcuts: If both are null, they are the same.
|
||||||
if (x == null && y == null) return 0;
|
if (x == null && y == null) return 0;
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public void AddSecondsString(string key, DateTime value)
|
public void AddSecondsString(string key, DateTime value)
|
||||||
{
|
{
|
||||||
Add(key, DateTimeConverter.ConvertToSeconds(value).ToString());
|
Add(key, DateTimeConverter.ConvertToSeconds(value).ToString()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -167,7 +167,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
public void AddOptionalSecondsString(string key, DateTime? value)
|
public void AddOptionalSecondsString(string key, DateTime? value)
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
Add(key, DateTimeConverter.ConvertToSeconds(value).ToString());
|
Add(key, DateTimeConverter.ConvertToSeconds(value).ToString()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -187,7 +187,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public void AddEnumAsInt<T>(string key, T value)
|
public void AddEnumAsInt<T>(string key, T value)
|
||||||
{
|
{
|
||||||
var stringVal = EnumConverter.GetString(value);
|
var stringVal = EnumConverter.GetString(value)!;
|
||||||
Add(key, int.Parse(stringVal)!);
|
Add(key, int.Parse(stringVal)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,9 +58,38 @@ namespace CryptoExchange.Net.Objects
|
|||||||
HttpMethodParameterPosition? parameterPosition = null,
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
ArrayParametersSerialization? arraySerialization = null,
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
bool? preventCaching = null)
|
bool? preventCaching = null)
|
||||||
|
=> GetOrCreate(method + path, method, path, rateLimitGate, weight, authenticated, limitGuard, requestBodyFormat, parameterPosition, arraySerialization, preventCaching);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">Request identifier</param>
|
||||||
|
/// <param name="method">The HttpMethod</param>
|
||||||
|
/// <param name="path">Endpoint path</param>
|
||||||
|
/// <param name="rateLimitGate">The rate limit gate</param>
|
||||||
|
/// <param name="limitGuard">The rate limit guard for this specific endpoint</param>
|
||||||
|
/// <param name="weight">Request weight</param>
|
||||||
|
/// <param name="authenticated">Endpoint is authenticated</param>
|
||||||
|
/// <param name="requestBodyFormat">Request body format</param>
|
||||||
|
/// <param name="parameterPosition">Parameter position</param>
|
||||||
|
/// <param name="arraySerialization">Array serialization type</param>
|
||||||
|
/// <param name="preventCaching">Prevent request caching</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public RequestDefinition GetOrCreate(
|
||||||
|
string identifier,
|
||||||
|
HttpMethod method,
|
||||||
|
string path,
|
||||||
|
IRateLimitGate? rateLimitGate,
|
||||||
|
int weight,
|
||||||
|
bool authenticated,
|
||||||
|
IRateLimitGuard? limitGuard = null,
|
||||||
|
RequestBodyFormat? requestBodyFormat = null,
|
||||||
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
|
bool? preventCaching = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_definitions.TryGetValue(method + path, out var def))
|
if (!_definitions.TryGetValue(identifier, out var def))
|
||||||
{
|
{
|
||||||
def = new RequestDefinition(path, method)
|
def = new RequestDefinition(path, method)
|
||||||
{
|
{
|
||||||
@@ -73,7 +102,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
ParameterPosition = parameterPosition,
|
ParameterPosition = parameterPosition,
|
||||||
PreventCaching = preventCaching ?? false
|
PreventCaching = preventCaching ?? false
|
||||||
};
|
};
|
||||||
_definitions.TryAdd(method + path, def);
|
_definitions.TryAdd(identifier, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
return def;
|
return def;
|
||||||
|
|||||||
@@ -82,12 +82,12 @@ namespace CryptoExchange.Net.Objects
|
|||||||
TimeSyncState.LastSyncTime = DateTime.UtcNow;
|
TimeSyncState.LastSyncTime = DateTime.UtcNow;
|
||||||
if (offset.TotalMilliseconds > 0 && offset.TotalMilliseconds < 500)
|
if (offset.TotalMilliseconds > 0 && offset.TotalMilliseconds < 500)
|
||||||
{
|
{
|
||||||
Logger.Log(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset within limits, set offset to 0ms");
|
Logger.Log(LogLevel.Information, "{TimeSyncState.ApiName} Time offset within limits, set offset to 0ms", TimeSyncState.ApiName);
|
||||||
TimeSyncState.TimeOffset = TimeSpan.Zero;
|
TimeSyncState.TimeOffset = TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Log(LogLevel.Information, $"{TimeSyncState.ApiName} Time offset set to {Math.Round(offset.TotalMilliseconds)}ms");
|
Logger.Log(LogLevel.Information, "{TimeSyncState.ApiName} Time offset set to {Offset}ms", TimeSyncState.ApiName, Math.Round(offset.TotalMilliseconds));
|
||||||
TimeSyncState.TimeOffset = offset;
|
TimeSyncState.TimeOffset = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -843,9 +843,9 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
|
|
||||||
internal class DescComparer<T> : IComparer<T>
|
internal class DescComparer<T> : IComparer<T>
|
||||||
{
|
{
|
||||||
public int Compare(T x, T y)
|
public int Compare(T? x, T? y)
|
||||||
{
|
{
|
||||||
return Comparer<T>.Default.Compare(y, x);
|
return Comparer<T>.Default.Compare(y!, x!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace CryptoExchange.Net.RateLimiting.Guards
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply guard per connection
|
/// Apply guard per connection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Func<RequestDefinition, string, string?, string> PerConnection { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => def.ConnectionId.ToString());
|
public static Func<RequestDefinition, string, string?, string> PerConnection { get; } = new Func<RequestDefinition, string, string?, string>((def, host, key) => def.ConnectionId.ToString()!);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply guard per API key
|
/// Apply guard per API key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -68,8 +68,9 @@ namespace CryptoExchange.Net.RateLimiting.Interfaces
|
|||||||
/// <param name="baseAddress">The host address</param>
|
/// <param name="baseAddress">The host address</param>
|
||||||
/// <param name="apiKey">The API key</param>
|
/// <param name="apiKey">The API key</param>
|
||||||
/// <param name="behaviour">Behaviour when rate limit is hit</param>
|
/// <param name="behaviour">Behaviour when rate limit is hit</param>
|
||||||
|
/// <param name="requestWeight">The weight to apply to the limit guard</param>
|
||||||
/// <param name="ct">Cancelation token</param>
|
/// <param name="ct">Cancelation token</param>
|
||||||
/// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns>
|
/// <returns>Error if RateLimitingBehaviour is Fail and rate limit is hit</returns>
|
||||||
Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, RateLimitingBehaviour behaviour, CancellationToken ct);
|
Task<CallResult> ProcessSingleAsync(ILogger logger, int itemId, IRateLimitGuard guard, RateLimitItemType type, RequestDefinition definition, string baseAddress, string? apiKey, int requestWeight, RateLimitingBehaviour behaviour, CancellationToken ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ namespace CryptoExchange.Net.RateLimiting
|
|||||||
RequestDefinition definition,
|
RequestDefinition definition,
|
||||||
string host,
|
string host,
|
||||||
string? apiKey,
|
string? apiKey,
|
||||||
|
int requestWeight,
|
||||||
RateLimitingBehaviour rateLimitingBehaviour,
|
RateLimitingBehaviour rateLimitingBehaviour,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
@@ -77,7 +78,7 @@ namespace CryptoExchange.Net.RateLimiting
|
|||||||
_waitingCount++;
|
_waitingCount++;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await CheckGuardsAsync(new IRateLimitGuard[] { guard }, logger, itemId, type, definition, host, apiKey, 1, rateLimitingBehaviour, ct).ConfigureAwait(false);
|
return await CheckGuardsAsync(new IRateLimitGuard[] { guard }, logger, itemId, type, definition, host, apiKey, requestWeight, rateLimitingBehaviour, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace CryptoExchange.Net.Requests
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Uri Uri => _request.RequestUri;
|
public Uri Uri => _request.RequestUri!;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int RequestId { get; }
|
public int RequestId { get; }
|
||||||
|
|||||||
@@ -17,28 +17,7 @@ namespace CryptoExchange.Net.Requests
|
|||||||
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null)
|
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null)
|
||||||
{
|
{
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
client = CreateClient(proxy, requestTimeout);
|
||||||
var handler = new HttpClientHandler();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
|
||||||
}
|
|
||||||
catch (PlatformNotSupportedException) { }
|
|
||||||
|
|
||||||
if (proxy != null)
|
|
||||||
{
|
|
||||||
handler.Proxy = new WebProxy
|
|
||||||
{
|
|
||||||
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
|
|
||||||
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
client = new HttpClient(handler)
|
|
||||||
{
|
|
||||||
Timeout = requestTimeout
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_httpClient = client;
|
_httpClient = client;
|
||||||
}
|
}
|
||||||
@@ -51,5 +30,37 @@ namespace CryptoExchange.Net.Requests
|
|||||||
|
|
||||||
return new Request(new HttpRequestMessage(method, uri), _httpClient, requestId);
|
return new Request(new HttpRequestMessage(method, uri), _httpClient, requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout)
|
||||||
|
{
|
||||||
|
_httpClient = CreateClient(proxy, requestTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpClient CreateClient(ApiProxy? proxy, TimeSpan requestTimeout)
|
||||||
|
{
|
||||||
|
var handler = new HttpClientHandler();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
|
||||||
|
handler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
|
||||||
|
}
|
||||||
|
catch (PlatformNotSupportedException) { }
|
||||||
|
|
||||||
|
if (proxy != null)
|
||||||
|
{
|
||||||
|
handler.Proxy = new WebProxy
|
||||||
|
{
|
||||||
|
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
|
||||||
|
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = new HttpClient(handler)
|
||||||
|
{
|
||||||
|
Timeout = requestTimeout
|
||||||
|
};
|
||||||
|
return client;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (param.Names.All(x => ExchangeParameters.HasValue(exchangeParameters, exchange, x, param.ValueType) != true))
|
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, exchange, x, param.ValueType) != true))
|
||||||
return new ArgumentError($"One of exchange parameters `{string.Join(", ", param.Names)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
|
return new ArgumentError($"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,13 +113,13 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(param.Name))
|
if (!string.IsNullOrEmpty(param.Name))
|
||||||
{
|
{
|
||||||
if (typeof(T).GetProperty(param.Name).GetValue(request, null) == null)
|
if (typeof(T).GetProperty(param.Name)!.GetValue(request, null) == null)
|
||||||
return new ArgumentError($"Required optional parameter `{param.Name}` for exchange `{exchange}` is missing. Example: {param.ExampleValue}");
|
return new ArgumentError($"Required optional parameter `{param.Name}` for exchange `{exchange}` is missing. Example: {param.ExampleValue}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (param.Names.All(x => typeof(T).GetProperty(param.Name).GetValue(request, null) == null))
|
if (param.Names!.All(x => typeof(T).GetProperty(param.Name!)!.GetValue(request, null) == null))
|
||||||
return new ArgumentError($"One of optional parameters `{string.Join(", ", param.Names)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
|
return new ArgumentError($"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{exchange}` should be provided. Example: {param.ExampleValue}");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
public override string ToString(string exchange)
|
public override string ToString(string exchange)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(base.ToString(exchange));
|
var sb = new StringBuilder(base.ToString(exchange));
|
||||||
sb.AppendLine($"Supported limit values: [{(SupportedLimits == null ? string.Join(", ", SupportedLimits) : $"{MinLimit}..{MaxLimit}")}]");
|
sb.AppendLine($"Supported limit values: [{(SupportedLimits != null ? string.Join(", ", SupportedLimits) : $"{MinLimit}..{MaxLimit}")}]");
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
{
|
{
|
||||||
if (Name != null)
|
if (Name != null)
|
||||||
return $"[{ValueType.Name}] {Name}: {Description} | example: {ExampleValue}";
|
return $"[{ValueType.Name}] {Name}: {Description} | example: {ExampleValue}";
|
||||||
return $"[{ValueType.Name}] {string.Join(" / ", Names)}: {Description} | example: {ExampleValue}";
|
return $"[{ValueType.Name}] {string.Join(" / ", Names!)}: {Description} | example: {ExampleValue}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? PriceDecimals { get; set; }
|
public int? PriceDecimals { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// The max amount of significant figures to use for price. For example with value of 5 these values are valid: 0.00001, 0.12300, 123.53, 12345, but this is not: 12345.1
|
||||||
|
/// </summary>
|
||||||
|
public int? PriceSignificantFigures { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// Whether the symbol is currently available for trading
|
/// Whether the symbol is currently available for trading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Trading { get; set; }
|
public bool Trading { get; set; }
|
||||||
|
|||||||
@@ -155,6 +155,12 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
_baseAddress = $"{Uri.Scheme}://{Uri.Host}";
|
_baseAddress = $"{Uri.Scheme}://{Uri.Host}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void UpdateProxy(ApiProxy? proxy)
|
||||||
|
{
|
||||||
|
Parameters.Proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual async Task<CallResult> ConnectAsync()
|
public virtual async Task<CallResult> ConnectAsync()
|
||||||
{
|
{
|
||||||
@@ -189,9 +195,12 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
socket.Options.SetBuffer(_receiveBufferSize, _sendBufferSize);
|
socket.Options.SetBuffer(_receiveBufferSize, _sendBufferSize);
|
||||||
if (Parameters.Proxy != null)
|
if (Parameters.Proxy != null)
|
||||||
SetProxy(socket, Parameters.Proxy);
|
SetProxy(socket, Parameters.Proxy);
|
||||||
#if NET6_0_OR_GREATER
|
#if NET6_0_OR_GREATER
|
||||||
socket.Options.CollectHttpResponseDetails = true;
|
socket.Options.CollectHttpResponseDetails = true;
|
||||||
#endif
|
#endif
|
||||||
|
#if NET9_0_OR_GREATER
|
||||||
|
socket.Options.KeepAliveTimeout = TimeSpan.FromSeconds(10);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
catch (PlatformNotSupportedException)
|
catch (PlatformNotSupportedException)
|
||||||
{
|
{
|
||||||
@@ -229,13 +238,13 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
if (e is WebSocketException we)
|
if (e is WebSocketException we)
|
||||||
{
|
{
|
||||||
#if (NET6_0_OR_GREATER)
|
#if (NET6_0_OR_GREATER)
|
||||||
if (_socket.HttpStatusCode == HttpStatusCode.TooManyRequests)
|
if (_socket.HttpStatusCode == HttpStatusCode.TooManyRequests)
|
||||||
{
|
{
|
||||||
await (OnConnectRateLimited?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
|
await (OnConnectRateLimited?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
|
||||||
return new CallResult(new ServerRateLimitError(we.Message));
|
return new CallResult(new ServerRateLimitError(we.Message));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// ClientWebSocket.HttpStatusCode is only available in .NET6+ https://learn.microsoft.com/en-us/dotnet/api/system.net.websockets.clientwebsocket.httpstatuscode?view=net-8.0
|
// ClientWebSocket.HttpStatusCode is only available in .NET6+ https://learn.microsoft.com/en-us/dotnet/api/system.net.websockets.clientwebsocket.httpstatuscode?view=net-8.0
|
||||||
// Try to read 429 from the message instead
|
// Try to read 429 from the message instead
|
||||||
if (we.Message.Contains("429"))
|
if (we.Message.Contains("429"))
|
||||||
@@ -243,7 +252,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
await (OnConnectRateLimited?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
|
await (OnConnectRateLimited?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
|
||||||
return new CallResult(new ServerRateLimitError(we.Message));
|
return new CallResult(new ServerRateLimitError(we.Message));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CallResult(new CantConnectError());
|
return new CallResult(new CantConnectError());
|
||||||
@@ -435,8 +444,8 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
{
|
{
|
||||||
// Wait until we receive close confirmation
|
// Wait until we receive close confirmation
|
||||||
await Task.Delay(10).ConfigureAwait(false);
|
await Task.Delay(10).ConfigureAwait(false);
|
||||||
if (DateTime.UtcNow - startWait > TimeSpan.FromSeconds(5))
|
if (DateTime.UtcNow - startWait > TimeSpan.FromSeconds(1))
|
||||||
break; // Wait for max 5 seconds, then just abort the connection
|
break; // Wait for max 1 second, then just abort the connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,15 +587,27 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
lock (_receivedMessagesLock)
|
lock (_receivedMessagesLock)
|
||||||
_receivedMessages.Add(new ReceiveItem(DateTime.UtcNow, receiveResult.Count));
|
_receivedMessages.Add(new ReceiveItem(DateTime.UtcNow, receiveResult.Count));
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
|
if (ex.InnerException?.InnerException?.Message.Equals("The WebSocket didn't recieve a Pong frame in response to a Ping frame within the configured KeepAliveTimeout.") == true)
|
||||||
|
{
|
||||||
|
// Spefic case that the websocket connection got closed because of a ping frame timeout
|
||||||
|
// Unfortunately doesn't seem to be a nicer way to catch
|
||||||
|
_logger.SocketPingTimeout(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_closeTask?.IsCompleted != false)
|
||||||
|
_closeTask = CloseInternalAsync();
|
||||||
|
|
||||||
// canceled
|
// canceled
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception wse)
|
catch (Exception wse)
|
||||||
{
|
{
|
||||||
// Connection closed unexpectedly
|
if (!_ctsSource.Token.IsCancellationRequested)
|
||||||
await (OnError?.Invoke(wse) ?? Task.CompletedTask).ConfigureAwait(false);
|
// Connection closed unexpectedly
|
||||||
|
await (OnError?.Invoke(wse) ?? Task.CompletedTask).ConfigureAwait(false);
|
||||||
|
|
||||||
if (_closeTask?.IsCompleted != false)
|
if (_closeTask?.IsCompleted != false)
|
||||||
_closeTask = CloseInternalAsync();
|
_closeTask = CloseInternalAsync();
|
||||||
break;
|
break;
|
||||||
@@ -598,14 +619,14 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (_socket.State == WebSocketState.CloseReceived)
|
if (_socket.State == WebSocketState.CloseReceived)
|
||||||
{
|
{
|
||||||
// Close received means it server initiated, we should send a confirmation and close the socket
|
// Close received means it server initiated, we should send a confirmation and close the socket
|
||||||
_logger.SocketReceivedCloseMessage(Id, receiveResult.CloseStatus.ToString(), receiveResult.CloseStatusDescription);
|
_logger.SocketReceivedCloseMessage(Id, receiveResult.CloseStatus.ToString()!, receiveResult.CloseStatusDescription ?? string.Empty);
|
||||||
if (_closeTask?.IsCompleted != false)
|
if (_closeTask?.IsCompleted != false)
|
||||||
_closeTask = CloseInternalAsync();
|
_closeTask = CloseInternalAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Means the socket is now closed and we were the one initiating it
|
// Means the socket is now closed and we were the one initiating it
|
||||||
_logger.SocketReceivedCloseConfirmation(Id, receiveResult.CloseStatus.ToString(), receiveResult.CloseStatusDescription);
|
_logger.SocketReceivedCloseConfirmation(Id, receiveResult.CloseStatus.ToString()!, receiveResult.CloseStatusDescription ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -620,7 +641,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
// Write the data to a memory stream to be reassembled later
|
// Write the data to a memory stream to be reassembled later
|
||||||
if (multipartStream == null)
|
if (multipartStream == null)
|
||||||
multipartStream = new MemoryStream();
|
multipartStream = new MemoryStream();
|
||||||
multipartStream.Write(buffer.Array, buffer.Offset, receiveResult.Count);
|
multipartStream.Write(buffer.Array!, buffer.Offset, receiveResult.Count);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -634,7 +655,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
{
|
{
|
||||||
// Received the end of a multipart message, write to memory stream for reassembling
|
// Received the end of a multipart message, write to memory stream for reassembling
|
||||||
_logger.SocketReceivedPartialMessage(Id, receiveResult.Count);
|
_logger.SocketReceivedPartialMessage(Id, receiveResult.Count);
|
||||||
multipartStream!.Write(buffer.Array, buffer.Offset, receiveResult.Count);
|
multipartStream!.Write(buffer.Array!, buffer.Offset, receiveResult.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Callback after query
|
/// Callback after query
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<CallResult>? Callback { get; set; }
|
public Action<SocketConnection, CallResult>? Callback { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Completed { get; set; }
|
public bool Completed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout for the request
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? RequestTimeout { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of required responses. Can be more than 1 when for example subscribing multiple symbols streams in a single request,
|
/// The number of required responses. Can be more than 1 when for example subscribing multiple symbols streams in a single request,
|
||||||
/// and each symbol receives it's own confirmation response
|
/// and each symbol receives it's own confirmation response
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ using System.Diagnostics;
|
|||||||
using CryptoExchange.Net.Clients;
|
using CryptoExchange.Net.Clients;
|
||||||
using CryptoExchange.Net.Logging.Extensions;
|
using CryptoExchange.Net.Logging.Extensions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using CryptoExchange.Net.Objects.Options;
|
||||||
|
using CryptoExchange.Net.Authentication;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
@@ -396,7 +398,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual Task HandleRequestRateLimitedAsync(int requestId)
|
protected virtual Task HandleRequestRateLimitedAsync(int requestId)
|
||||||
{
|
{
|
||||||
Query query;
|
Query? query;
|
||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
{
|
{
|
||||||
query = _listeners.OfType<Query>().FirstOrDefault(x => x.Id == requestId);
|
query = _listeners.OfType<Query>().FirstOrDefault(x => x.Id == requestId);
|
||||||
@@ -425,7 +427,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <param name="requestId">Id of the request sent</param>
|
/// <param name="requestId">Id of the request sent</param>
|
||||||
protected virtual Task HandleRequestSentAsync(int requestId)
|
protected virtual Task HandleRequestSentAsync(int requestId)
|
||||||
{
|
{
|
||||||
Query query;
|
Query? query;
|
||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
{
|
{
|
||||||
query = _listeners.OfType<Query>().FirstOrDefault(x => x.Id == requestId);
|
query = _listeners.OfType<Query>().FirstOrDefault(x => x.Id == requestId);
|
||||||
@@ -437,7 +439,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.IsSend(ApiClient.ClientOptions.RequestTimeout);
|
query.IsSend(query.RequestTimeout ?? ApiClient.ClientOptions.RequestTimeout);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,6 +585,16 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task TriggerReconnectAsync() => await _socket.ReconnectAsync().ConfigureAwait(false);
|
public async Task TriggerReconnectAsync() => await _socket.ReconnectAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the proxy setting and reconnect
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="proxy">New proxy setting</param>
|
||||||
|
public async Task UpdateProxy(ApiProxy? proxy)
|
||||||
|
{
|
||||||
|
_socket.UpdateProxy(proxy);
|
||||||
|
await TriggerReconnectAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Close the connection
|
/// Close the connection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -988,7 +1000,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <param name="interval">How often</param>
|
/// <param name="interval">How often</param>
|
||||||
/// <param name="queryDelegate">Method returning the query to send</param>
|
/// <param name="queryDelegate">Method returning the query to send</param>
|
||||||
/// <param name="callback">The callback for processing the response</param>
|
/// <param name="callback">The callback for processing the response</param>
|
||||||
public virtual void QueryPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<CallResult>? callback)
|
public virtual void QueryPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, Query> queryDelegate, Action<SocketConnection, CallResult>? callback)
|
||||||
{
|
{
|
||||||
if (queryDelegate == null)
|
if (queryDelegate == null)
|
||||||
throw new ArgumentNullException(nameof(queryDelegate));
|
throw new ArgumentNullException(nameof(queryDelegate));
|
||||||
@@ -1020,7 +1032,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await SendAndWaitQueryAsync(query).ConfigureAwait(false);
|
var result = await SendAndWaitQueryAsync(query).ConfigureAwait(false);
|
||||||
callback?.Invoke(result);
|
callback?.Invoke(this, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
if (objectValue is bool boolVal && jsonValue.Value<bool>() != boolVal)
|
if (objectValue is bool boolVal && jsonValue.Value<bool>() != boolVal)
|
||||||
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<bool>()} vs {(bool)objectValue}");
|
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<bool>()} vs {(bool)objectValue}");
|
||||||
|
|
||||||
if (jsonValue.Value<bool>() != bool.Parse(objectValue.ToString()))
|
if (jsonValue.Value<bool>() != bool.Parse(objectValue.ToString()!))
|
||||||
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<bool>()} vs {(bool)objectValue}");
|
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<bool>()} vs {(bool)objectValue}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var item in jObj.Children())
|
foreach (var item in jObj.Children())
|
||||||
{
|
{
|
||||||
var arrayProp = resultProps.Where(p => p.Item2 != null).SingleOrDefault(p => p.Item2!.Index == i).p;
|
var arrayProp = resultProps.Where(p => p.Item2 != null).FirstOrDefault(p => p.Item2!.Index == i).p;
|
||||||
if (arrayProp != null)
|
if (arrayProp != null)
|
||||||
CheckPropertyValue(method, item, arrayProp.GetValue(resultObj), arrayProp.PropertyType, arrayProp.Name, "Array index " + i, ignoreProperties!);
|
CheckPropertyValue(method, item, arrayProp.GetValue(resultObj), arrayProp.PropertyType, arrayProp.Name, "Array index " + i, ignoreProperties!);
|
||||||
i++;
|
i++;
|
||||||
@@ -211,7 +211,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
|
|
||||||
if (dictProp.Value.Type == JTokenType.Object)
|
if (dictProp.Value.Type == JTokenType.Object)
|
||||||
{
|
{
|
||||||
CheckPropertyValue(method, dictProp.Value, dict[dictProp.Name]!, dict[dictProp.Name].GetType(), null, null, ignoreProperties);
|
CheckPropertyValue(method, dictProp.Value, dict[dictProp.Name]!, dict[dictProp.Name]!.GetType(), null, null, ignoreProperties);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -264,9 +264,9 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var item in jtoken.Children())
|
foreach (var item in jtoken.Children())
|
||||||
{
|
{
|
||||||
var arrayProp = resultProps.Where(p => p.Item2 != null).SingleOrDefault(p => p.Item2!.Index == i).p;
|
var arrayProp = resultProps.Where(p => p.Item2 != null).FirstOrDefault(p => p.Item2!.Index == i).p;
|
||||||
if (arrayProp != null)
|
if (arrayProp != null)
|
||||||
CheckPropertyValue(method, item, arrayProp.GetValue(resultObj), propertyType, arrayProp.Name, "Array index " + i, ignoreProperties);
|
CheckPropertyValue(method, item, arrayProp.GetValue(resultObj), arrayProp.PropertyType, arrayProp.Name, "Array index " + i, ignoreProperties);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -350,7 +350,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var item in jObjs.Children())
|
foreach (var item in jObjs.Children())
|
||||||
{
|
{
|
||||||
var arrayProp = resultProps.Where(p => p.Item2 != null).SingleOrDefault(p => p.Item2!.Index == i).p;
|
var arrayProp = resultProps.Where(p => p.Item2 != null).FirstOrDefault(p => p.Item2!.Index == i).p;
|
||||||
if (arrayProp != null)
|
if (arrayProp != null)
|
||||||
CheckPropertyValue(method, item, arrayProp.GetValue(propertyValue), arrayProp.PropertyType, arrayProp.Name, "Array index " + i, ignoreProperties!);
|
CheckPropertyValue(method, item, arrayProp.GetValue(propertyValue), arrayProp.PropertyType, arrayProp.Name, "Array index " + i, ignoreProperties!);
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ namespace CryptoExchange.Net.Testing
|
|||||||
{
|
{
|
||||||
internal class EnumValueTraceListener : TraceListener
|
internal class EnumValueTraceListener : TraceListener
|
||||||
{
|
{
|
||||||
public override void Write(string message)
|
public override void Write(string? message)
|
||||||
{
|
{
|
||||||
|
if (message == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (message.Contains("Cannot map"))
|
if (message.Contains("Cannot map"))
|
||||||
throw new Exception("Enum value error: " + message);
|
throw new Exception("Enum value error: " + message);
|
||||||
|
|
||||||
@@ -14,8 +17,11 @@ namespace CryptoExchange.Net.Testing
|
|||||||
throw new Exception("Enum null error: " + message);
|
throw new Exception("Enum null error: " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteLine(string message)
|
public override void WriteLine(string? message)
|
||||||
{
|
{
|
||||||
|
if (message == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (message.Contains("Cannot map"))
|
if (message.Contains("Cannot map"))
|
||||||
throw new Exception("Enum value error: " + message);
|
throw new Exception("Enum value error: " + message);
|
||||||
|
|
||||||
|
|||||||
@@ -25,5 +25,7 @@ namespace CryptoExchange.Net.Testing.Implementations
|
|||||||
_request.RequestId = requestId;
|
_request.RequestId = requestId;
|
||||||
return _request;
|
return _request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,5 +92,7 @@ namespace CryptoExchange.Net.Testing.Implementations
|
|||||||
|
|
||||||
public Task ReconnectAsync() => throw new NotImplementedException();
|
public Task ReconnectAsync() => throw new NotImplementedException();
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void UpdateProxy(ApiProxy? proxy) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,9 +150,9 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TClient"></typeparam>
|
/// <typeparam name="TClient"></typeparam>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public static void CheckForMissingRestInterfaces<TClient>()
|
public static void CheckForMissingRestInterfaces<TClient>(string[]? excludeInterfaces = null)
|
||||||
{
|
{
|
||||||
CheckForMissingInterfaces(typeof(TClient), typeof(Task));
|
CheckForMissingInterfaces(typeof(TClient), typeof(Task), excludeInterfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -160,28 +160,32 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TClient"></typeparam>
|
/// <typeparam name="TClient"></typeparam>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public static void CheckForMissingSocketInterfaces<TClient>()
|
public static void CheckForMissingSocketInterfaces<TClient>(string[]? excludeInterfaces = null)
|
||||||
{
|
{
|
||||||
CheckForMissingInterfaces(typeof(TClient), typeof(Task<CallResult<UpdateSubscription>>));
|
CheckForMissingInterfaces(typeof(TClient), typeof(Task<CallResult<UpdateSubscription>>), excludeInterfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckForMissingInterfaces(Type clientType, Type implementationTypes)
|
private static void CheckForMissingInterfaces(Type clientType, Type implementationTypes, string[]? excludeInterfaces = null)
|
||||||
{
|
{
|
||||||
var assembly = Assembly.GetAssembly(clientType);
|
var assembly = Assembly.GetAssembly(clientType);
|
||||||
var interfaceType = clientType.GetInterface("I" + clientType.Name);
|
var interfaceType = clientType.GetInterface("I" + clientType.Name);
|
||||||
var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("I" + clientType.Name) && !t.Name.EndsWith("Shared"));
|
var clientInterfaces = assembly!.GetTypes()
|
||||||
|
.Where(t => t.Name.StartsWith("I" + clientType.Name)
|
||||||
|
&& !t.Name.EndsWith("Shared")
|
||||||
|
&& (excludeInterfaces?.Contains(t.Name) != true));
|
||||||
|
|
||||||
foreach (var clientInterface in clientInterfaces)
|
foreach (var clientInterface in clientInterfaces)
|
||||||
{
|
{
|
||||||
var implementations = assembly.GetTypes().Where(t => clientInterface.IsAssignableFrom(t) && t != clientInterface);
|
var implementations = assembly.GetTypes().Where(t => clientInterface.IsAssignableFrom(t) && !t.IsInterface && t != clientInterface);
|
||||||
foreach (var implementation in implementations)
|
foreach (var implementation in implementations)
|
||||||
{
|
{
|
||||||
int methods = 0;
|
int methods = 0;
|
||||||
foreach (var method in implementation.GetMethods().Where(m => implementationTypes.IsAssignableFrom(m.ReturnType)))
|
foreach (var method in implementation.GetMethods().Where(m => implementationTypes.IsAssignableFrom(m.ReturnType)))
|
||||||
{
|
{
|
||||||
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
|
var interfaceMethod =
|
||||||
if (interfaceMethod == null)
|
clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray())
|
||||||
throw new Exception($"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
|
?? clientInterface.GetInterfaces().Select(x => x.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray())).FirstOrDefault()
|
||||||
|
?? throw new Exception($"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
|
||||||
methods++;
|
methods++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -350,7 +350,8 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
_data.Add(item);
|
_data.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_firstTimestamp = _data.Min(v => v.Timestamp);
|
if (_data.Any())
|
||||||
|
_firstTimestamp = _data.Min(v => v.Timestamp);
|
||||||
|
|
||||||
ApplyWindow(false);
|
ApplyWindow(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,23 +5,24 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="10.9.0" />
|
<PackageReference Include="Binance.Net" Version="10.16.1" />
|
||||||
<PackageReference Include="Bitfinex.Net" Version="7.10.0" />
|
<PackageReference Include="Bitfinex.Net" Version="7.13.1" />
|
||||||
<PackageReference Include="BitMart.Net" Version="1.7.0" />
|
<PackageReference Include="BitMart.Net" Version="1.12.1" />
|
||||||
<PackageReference Include="Bybit.Net" Version="3.16.0" />
|
<PackageReference Include="Bybit.Net" Version="4.0.2" />
|
||||||
<PackageReference Include="CoinEx.Net" Version="7.9.0" />
|
<PackageReference Include="CoinEx.Net" Version="7.13.2" />
|
||||||
<PackageReference Include="CryptoCom.Net" Version="1.2.0" />
|
<PackageReference Include="CryptoCom.Net" Version="1.5.1" />
|
||||||
<PackageReference Include="GateIo.Net" Version="1.12.0" />
|
<PackageReference Include="GateIo.Net" Version="1.17.1" />
|
||||||
<PackageReference Include="JK.BingX.Net" Version="1.14.0" />
|
<PackageReference Include="HyperLiquid.Net" Version="1.0.0" />
|
||||||
<PackageReference Include="JK.Bitget.Net" Version="1.13.0" />
|
<PackageReference Include="JK.BingX.Net" Version="1.19.1" />
|
||||||
<PackageReference Include="JK.Mexc.Net" Version="1.11.0" />
|
<PackageReference Include="JK.Bitget.Net" Version="1.19.1" />
|
||||||
<PackageReference Include="JK.OKX.Net" Version="2.8.0" />
|
<PackageReference Include="JK.Mexc.Net" Version="1.15.1" />
|
||||||
<PackageReference Include="JKorf.Coinbase.Net" Version="1.4.0" />
|
<PackageReference Include="JK.OKX.Net" Version="2.14.1" />
|
||||||
<PackageReference Include="JKorf.HTX.Net" Version="6.4.0" />
|
<PackageReference Include="JKorf.Coinbase.Net" Version="1.7.2" />
|
||||||
<PackageReference Include="KrakenExchange.Net" Version="5.2.0" />
|
<PackageReference Include="JKorf.HTX.Net" Version="6.8.1" />
|
||||||
<PackageReference Include="Kucoin.Net" Version="5.18.0" />
|
<PackageReference Include="KrakenExchange.Net" Version="5.5.3" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
<PackageReference Include="Kucoin.Net" Version="5.23.4" />
|
||||||
<PackageReference Include="WhiteBit.Net" Version="1.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
|
<PackageReference Include="WhiteBit.Net" Version="1.3.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
@inject ICoinExRestClient coinexClient
|
@inject ICoinExRestClient coinexClient
|
||||||
@inject ICryptoComRestClient cryptocomClient
|
@inject ICryptoComRestClient cryptocomClient
|
||||||
@inject IGateIoRestClient gateioClient
|
@inject IGateIoRestClient gateioClient
|
||||||
@inject IHTXRestClient huobiClient
|
@inject IHTXRestClient htxClient
|
||||||
|
@inject IHyperLiquidRestClient hyperLiquidClient
|
||||||
@inject IKrakenRestClient krakenClient
|
@inject IKrakenRestClient krakenClient
|
||||||
@inject IKucoinRestClient kucoinClient
|
@inject IKucoinRestClient kucoinClient
|
||||||
@inject IMexcRestClient mexcClient
|
@inject IMexcRestClient mexcClient
|
||||||
@@ -37,7 +38,8 @@
|
|||||||
var coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
var cryptocomTask = cryptocomClient.ExchangeApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
var cryptocomTask = cryptocomClient.ExchangeApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
||||||
var gateioTask = gateioClient.SpotApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
var gateioTask = gateioClient.SpotApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
||||||
var htxTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
var htxTask = htxClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
||||||
|
var hyperLiquidTask = hyperLiquidClient.FuturesApi.ExchangeData.GetExchangeInfoAndTickersAsync(); // HyperLiquid does not have BTC spot trading
|
||||||
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
|
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
|
||||||
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
||||||
var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
@@ -79,6 +81,13 @@
|
|||||||
if (htxTask.Result.Success)
|
if (htxTask.Result.Success)
|
||||||
_prices.Add("HTX", htxTask.Result.Data.ClosePrice ?? 0);
|
_prices.Add("HTX", htxTask.Result.Data.ClosePrice ?? 0);
|
||||||
|
|
||||||
|
if (hyperLiquidTask.Result.Success)
|
||||||
|
{
|
||||||
|
// HyperLiquid API doesn't offer an endpoint to filter for a specific ticker, so we have to filter client side
|
||||||
|
var tickers = hyperLiquidTask.Result.Data.Tickers;
|
||||||
|
_prices.Add("HyperLiquid", tickers.Single(x => x.Symbol == "BTC").MidPrice ?? 9);
|
||||||
|
}
|
||||||
|
|
||||||
if (krakenTask.Result.Success)
|
if (krakenTask.Result.Success)
|
||||||
_prices.Add("Kraken", krakenTask.Result.Data.First().Value.LastTrade.Price);
|
_prices.Add("Kraken", krakenTask.Result.Data.First().Value.LastTrade.Price);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@inject ICryptoComSocketClient cryptocomSocketClient
|
@inject ICryptoComSocketClient cryptocomSocketClient
|
||||||
@inject IGateIoSocketClient gateioSocketClient
|
@inject IGateIoSocketClient gateioSocketClient
|
||||||
@inject IHTXSocketClient htxSocketClient
|
@inject IHTXSocketClient htxSocketClient
|
||||||
|
@inject IHyperLiquidSocketClient hyperLiquidSocketClient
|
||||||
@inject IKrakenSocketClient krakenSocketClient
|
@inject IKrakenSocketClient krakenSocketClient
|
||||||
@inject IKucoinSocketClient kucoinSocketClient
|
@inject IKucoinSocketClient kucoinSocketClient
|
||||||
@inject IMexcSocketClient mexcSocketClient
|
@inject IMexcSocketClient mexcSocketClient
|
||||||
@@ -42,10 +43,12 @@
|
|||||||
bitmartSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("BitMart", data.Data.LastPrice)),
|
bitmartSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("BitMart", data.Data.LastPrice)),
|
||||||
bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
|
bybitSocketClient.V5SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bybit", data.Data.LastPrice)),
|
||||||
coinExSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)),
|
coinExSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("CoinEx", data.Data.LastPrice)),
|
||||||
coinbaseSocketClient.AdvancedTradeApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Coinbase", data.Data.LastPrice)),
|
coinbaseSocketClient.AdvancedTradeApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Coinbase", data.Data.LastPrice ?? 0)),
|
||||||
cryptocomSocketClient.ExchangeApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("CryptoCom", data.Data.LastPrice ?? 0)),
|
cryptocomSocketClient.ExchangeApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("CryptoCom", data.Data.LastPrice ?? 0)),
|
||||||
gateioSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("GateIo", data.Data.LastPrice)),
|
gateioSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("GateIo", data.Data.LastPrice)),
|
||||||
htxSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("HTX", data.Data.ClosePrice ?? 0)),
|
htxSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("HTX", data.Data.ClosePrice ?? 0)),
|
||||||
|
// HyperLiquid doesn't support the ETH/BTC pair
|
||||||
|
//hyperLiquidSocketClient.SpotApi.SubscribeToSymbolUpdatesAsync("ETH", data => UpdateData("HyperLiquid", data.Data.MidPrice ?? 0)),
|
||||||
krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastPrice)),
|
krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastPrice)),
|
||||||
kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
|
kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
|
||||||
mexcSocketClient.SpotApi.SubscribeToMiniTickerUpdatesAsync("ETHBTC", data => UpdateData("Mexc", data.Data.LastPrice)),
|
mexcSocketClient.SpotApi.SubscribeToMiniTickerUpdatesAsync("ETHBTC", data => UpdateData("Mexc", data.Data.LastPrice)),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
@using CryptoCom.Net.Interfaces
|
@using CryptoCom.Net.Interfaces
|
||||||
@using GateIo.Net.Interfaces
|
@using GateIo.Net.Interfaces
|
||||||
@using HTX.Net.Interfaces
|
@using HTX.Net.Interfaces
|
||||||
|
@using HyperLiquid.Net.Interfaces
|
||||||
@using Kraken.Net.Interfaces
|
@using Kraken.Net.Interfaces
|
||||||
@using Kucoin.Net.Clients
|
@using Kucoin.Net.Clients
|
||||||
@using Kucoin.Net.Interfaces
|
@using Kucoin.Net.Interfaces
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
@inject ICryptoComOrderBookFactory cryptocomFactory
|
@inject ICryptoComOrderBookFactory cryptocomFactory
|
||||||
@inject IGateIoOrderBookFactory gateioFactory
|
@inject IGateIoOrderBookFactory gateioFactory
|
||||||
@inject IHTXOrderBookFactory htxFactory
|
@inject IHTXOrderBookFactory htxFactory
|
||||||
|
@inject IHyperLiquidOrderBookFactory hyperLiquidFactory
|
||||||
@inject IKrakenOrderBookFactory krakenFactory
|
@inject IKrakenOrderBookFactory krakenFactory
|
||||||
@inject IKucoinOrderBookFactory kucoinFactory
|
@inject IKucoinOrderBookFactory kucoinFactory
|
||||||
@inject IMexcOrderBookFactory mexcFactory
|
@inject IMexcOrderBookFactory mexcFactory
|
||||||
@@ -79,6 +81,8 @@
|
|||||||
{ "CryptoCom", cryptocomFactory.Create("ETH_BTC") },
|
{ "CryptoCom", cryptocomFactory.Create("ETH_BTC") },
|
||||||
{ "GateIo", gateioFactory.CreateSpot("ETH_BTC") },
|
{ "GateIo", gateioFactory.CreateSpot("ETH_BTC") },
|
||||||
{ "HTX", htxFactory.CreateSpot("ethbtc") },
|
{ "HTX", htxFactory.CreateSpot("ethbtc") },
|
||||||
|
// HyperLiquid does not support the ETH/BTC pair
|
||||||
|
//{ "HyperLiquid", hyperLiquidFactory.Create("ETH/BTC") },
|
||||||
{ "Kraken", krakenFactory.CreateSpot("ETH/BTC") },
|
{ "Kraken", krakenFactory.CreateSpot("ETH/BTC") },
|
||||||
{ "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
|
{ "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
|
||||||
{ "Mexc", mexcFactory.CreateSpot("ETHBTC") },
|
{ "Mexc", mexcFactory.CreateSpot("ETHBTC") },
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
@using CryptoExchange.Net.Trackers.Trades
|
@using CryptoExchange.Net.Trackers.Trades
|
||||||
@using GateIo.Net.Interfaces
|
@using GateIo.Net.Interfaces
|
||||||
@using HTX.Net.Interfaces
|
@using HTX.Net.Interfaces
|
||||||
|
@using HyperLiquid.Net.Interfaces
|
||||||
@using Kraken.Net.Interfaces
|
@using Kraken.Net.Interfaces
|
||||||
@using Kucoin.Net.Clients
|
@using Kucoin.Net.Clients
|
||||||
@using Kucoin.Net.Interfaces
|
@using Kucoin.Net.Interfaces
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
@inject ICryptoComTrackerFactory cryptocomFactory
|
@inject ICryptoComTrackerFactory cryptocomFactory
|
||||||
@inject IGateIoTrackerFactory gateioFactory
|
@inject IGateIoTrackerFactory gateioFactory
|
||||||
@inject IHTXTrackerFactory htxFactory
|
@inject IHTXTrackerFactory htxFactory
|
||||||
|
@inject IHyperLiquidTrackerFactory hyperLiquidFactory
|
||||||
@inject IKrakenTrackerFactory krakenFactory
|
@inject IKrakenTrackerFactory krakenFactory
|
||||||
@inject IKucoinTrackerFactory kucoinFactory
|
@inject IKucoinTrackerFactory kucoinFactory
|
||||||
@inject IMexcTrackerFactory mexcFactory
|
@inject IMexcTrackerFactory mexcFactory
|
||||||
@@ -59,26 +61,28 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
var usdtSymbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||||
|
|
||||||
_trackers = new List<ITradeTracker>
|
_trackers = new List<ITradeTracker>
|
||||||
{
|
{
|
||||||
{ binanceFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ binanceFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bingXFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bingXFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitfinexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitfinexFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitgetFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitgetFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitmartFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitmartFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bybitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bybitFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ coinbaseFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ coinbaseFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ coinExFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ coinExFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ cryptocomFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ cryptocomFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ gateioFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ gateioFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ htxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ htxFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ krakenFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
// HyperLiquid doesn't support spot pair, but does have a futures BTC/USDC pair
|
||||||
{ kucoinFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ hyperLiquidFactory.CreateTradeTracker(new SharedSymbol(TradingMode.PerpetualLinear, "BTC", "USDC"), period: TimeSpan.FromMinutes(5)) },
|
||||||
{ mexcFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ krakenFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ okxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ kucoinFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ whitebitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ mexcFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
|
{ okxFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
|
{ whitebitFactory.CreateTradeTracker(usdtSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAll(_trackers.Select(b => b.StartAsync()));
|
await Task.WhenAll(_trackers.Select(b => b.StartAsync()));
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ namespace BlazorClient
|
|||||||
services.AddCoinEx();
|
services.AddCoinEx();
|
||||||
services.AddCryptoCom();
|
services.AddCryptoCom();
|
||||||
services.AddGateIo();
|
services.AddGateIo();
|
||||||
|
services.AddHyperLiquid();
|
||||||
services.AddHTX();
|
services.AddHTX();
|
||||||
services.AddKraken();
|
services.AddKraken();
|
||||||
services.AddKucoin();
|
services.AddKucoin();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
@using CryptoCom.Net.Interfaces.Clients;
|
@using CryptoCom.Net.Interfaces.Clients;
|
||||||
@using GateIo.Net.Interfaces.Clients;
|
@using GateIo.Net.Interfaces.Clients;
|
||||||
@using HTX.Net.Interfaces.Clients;
|
@using HTX.Net.Interfaces.Clients;
|
||||||
|
@using HyperLiquid.Net.Interfaces.Clients;
|
||||||
@using Kraken.Net.Interfaces.Clients;
|
@using Kraken.Net.Interfaces.Clients;
|
||||||
@using Kucoin.Net.Interfaces.Clients;
|
@using Kucoin.Net.Interfaces.Clients;
|
||||||
@using Mexc.Net.Interfaces.Clients;
|
@using Mexc.Net.Interfaces.Clients;
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ The following API's are directly supported. Note that there are 3rd party implem
|
|||||||
|Crypto.com|[JKorf/CryptoCom.Net](https://github.com/JKorf/CryptoCom.Net)|[](https://www.nuget.org/packages/CryptoCom.Net)|
|
|Crypto.com|[JKorf/CryptoCom.Net](https://github.com/JKorf/CryptoCom.Net)|[](https://www.nuget.org/packages/CryptoCom.Net)|
|
||||||
|Gate.io|[JKorf/GateIo.Net](https://github.com/JKorf/GateIo.Net)|[](https://www.nuget.org/packages/GateIo.Net)|
|
|Gate.io|[JKorf/GateIo.Net](https://github.com/JKorf/GateIo.Net)|[](https://www.nuget.org/packages/GateIo.Net)|
|
||||||
|HTX|[JKorf/HTX.Net](https://github.com/JKorf/HTX.Net)|[](https://www.nuget.org/packages/JKorf.HTX.Net)|
|
|HTX|[JKorf/HTX.Net](https://github.com/JKorf/HTX.Net)|[](https://www.nuget.org/packages/JKorf.HTX.Net)|
|
||||||
|
|HyperLiquid|[JKorf/HyperLiquid.Net](https://github.com/JKorf/HyperLiquid.Net)|[](https://www.nuget.org/packages/HyperLiquid.Net)|
|
||||||
|Kraken|[JKorf/Kraken.Net](https://github.com/JKorf/Kraken.Net)|[](https://www.nuget.org/packages/KrakenExchange.Net)|
|
|Kraken|[JKorf/Kraken.Net](https://github.com/JKorf/Kraken.Net)|[](https://www.nuget.org/packages/KrakenExchange.Net)|
|
||||||
|Kucoin|[JKorf/Kucoin.Net](https://github.com/JKorf/Kucoin.Net)|[](https://www.nuget.org/packages/Kucoin.Net)|
|
|Kucoin|[JKorf/Kucoin.Net](https://github.com/JKorf/Kucoin.Net)|[](https://www.nuget.org/packages/Kucoin.Net)|
|
||||||
|Mexc|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|
|
|Mexc|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|
|
||||||
|OKX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|
|
|OKX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|
|
||||||
|WhiteBit|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[](https://www.nuget.org/packages/WhiteBit.Net)|
|
|WhiteBit|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[](https://www.nuget.org/packages/WhiteBit.Net)|
|
||||||
|
|XT|[JKorf/XT.Net](https://github.com/JKorf/XT.Net)|[](https://www.nuget.org/packages/XT.Net)|
|
||||||
|
|
||||||
Any of these can be installed independently or install [CryptoClients.Net](https://github.com/jkorf/CryptoClients.Net) which includes all exchange API's.
|
Any of these can be installed independently or install [CryptoClients.Net](https://github.com/jkorf/CryptoClients.Net) which includes all exchange API's.
|
||||||
|
|
||||||
@@ -39,6 +41,22 @@ A Discord server is available [here](https://discord.gg/MSpeEtSY8t). Feel free t
|
|||||||
## Support the project
|
## Support the project
|
||||||
Any support is greatly appreciated.
|
Any support is greatly appreciated.
|
||||||
|
|
||||||
|
## Referral
|
||||||
|
When creating an account on new exchanges please consider using a referral link from below to support development
|
||||||
|
|
||||||
|
|Exchange|Link|
|
||||||
|
|--|--|
|
||||||
|
|Bybit|[https://partner.bybit.com/b/jkorf](https://partner.bybit.com/b/jkorf)|
|
||||||
|
|Coinbase|[https://advanced.coinbase.com/join/T6H54H8](https://advanced.coinbase.com/join/T6H54H8)|
|
||||||
|
|CoinEx|[https://www.coinex.com/register?refer_code=hd6gn](https://www.coinex.com/register?refer_code=hd6gn)|
|
||||||
|
|Crypto.com|[https://crypto.com/exch/26ge92xbkn](https://crypto.com/exch/26ge92xbkn)|
|
||||||
|
|HTX|[https://www.htx.com/invite/en-us/1f?invite_code=fxp9](https://www.htx.com/invite/en-us/1f?invite_code=fxp9)|
|
||||||
|
|HyperLiquid|[https://app.hyperliquid.xyz/join/JKORF](https://app.hyperliquid.xyz/join/JKORF)|
|
||||||
|
|Kucoin|[https://www.kucoin.com/r/rf/QBS4FPED](https://www.kucoin.com/r/rf/QBS4FPED)|
|
||||||
|
|OKX|[https://okx.com/join/48046699](https://okx.com/join/48046699)|
|
||||||
|
|WhiteBit|[https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf](https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf)|
|
||||||
|
|XT|[https://www.xt.com/en/accounts/register?ref=1HRM5J](https://www.xt.com/en/accounts/register?ref=1HRM5J)|
|
||||||
|
|
||||||
### Donate
|
### Donate
|
||||||
Make a one time donation in a crypto currency of your choice. If you prefer to donate a currency not listed here please contact me.
|
Make a one time donation in a crypto currency of your choice. If you prefer to donate a currency not listed here please contact me.
|
||||||
|
|
||||||
@@ -50,6 +68,47 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
|
|||||||
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
* Version 8.7.2 - 27 Jan 2025
|
||||||
|
* Some small fixes in the System.Text.Json ArrayConverter
|
||||||
|
* Added support for Flags enum deserialization in System.Text.Json EnumConverter
|
||||||
|
|
||||||
|
* Version 8.7.1 - 24 Jan 2025
|
||||||
|
* Added Authenticated property to IBaseApiClient interface to check if a client was provided API credentials
|
||||||
|
|
||||||
|
* Version 8.7.0 - 21 Jan 2025
|
||||||
|
* Added GetMillisecondTimestampLong helper method to AuthenticationProvider
|
||||||
|
* Added PriceSignificationFigures to SharedSpotSymbol model
|
||||||
|
|
||||||
|
* Version 8.6.1 - 09 Jan 2025
|
||||||
|
* Fixed websocket connection getting stuck after a ping frame timeout
|
||||||
|
* Removed websocket Error callback when exception is expected
|
||||||
|
* Removed unnecessary type restraints on RestApiClient.SendAsync methods
|
||||||
|
|
||||||
|
* Version 8.6.0 - 07 Jan 2025
|
||||||
|
* Added support for passing weight to apply to an individual ratelimit guard
|
||||||
|
* Added IFeeRestClient to service registration
|
||||||
|
* Added response headers parameter to RestApiClient.TryParseError method
|
||||||
|
* Added check for ServerRateLimitError on RestApiClient.TryParseError response
|
||||||
|
* Added ExchangeType Enum
|
||||||
|
* Some small improvements
|
||||||
|
|
||||||
|
* Version 8.5.0 - 23 Dec 2024
|
||||||
|
* Added SetOptions method to update client settings
|
||||||
|
* Added SocketConnection parameter to PeriodicQuery callback
|
||||||
|
* Added setting of DefaultProxyCredentials on HttpClient instance when client is not provided by DI
|
||||||
|
* Added support for overriding request timeout per request
|
||||||
|
* Added build target for net9.0
|
||||||
|
* Added setting of KeepAliveTimeout on websocket connections to improve dropped connection detection
|
||||||
|
* Changed max wait time for close handshake response from 5 seconds to 1 second
|
||||||
|
* Fixed exception in trade tracker when there is no data in the initial snapshot
|
||||||
|
|
||||||
|
* Version 8.4.5 - 20 Dec 2024
|
||||||
|
* Added EmptyArrayObjectConverter System.Text.Json JsonConverter
|
||||||
|
* Added JsonSerializerOptions parameter to SystemTextJsonMessageAccessor constructor
|
||||||
|
|
||||||
|
* Version 8.4.4 - 08 Dec 2024
|
||||||
|
* Changed JsonConverterCtorAttribute to use constructor type parameter instead of generic type parameter to support .net framework
|
||||||
|
|
||||||
* Version 8.4.3 - 03 Dec 2024
|
* Version 8.4.3 - 03 Dec 2024
|
||||||
* Fixed KlineTracker update handling
|
* Fixed KlineTracker update handling
|
||||||
|
|
||||||
|
|||||||
+490
-7
@@ -158,11 +158,13 @@
|
|||||||
<tr><td>Crypto.com</td><td><a href="https://github.com/JKorf/CryptoCom.Net">JKorf/CryptoCom.Net</a></td><td><a href="https://www.nuget.org/packages/CryptoCom.Net" target="_blank"><img src="https://img.shields.io/nuget/v/CryptoCom.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>Crypto.com</td><td><a href="https://github.com/JKorf/CryptoCom.Net">JKorf/CryptoCom.Net</a></td><td><a href="https://www.nuget.org/packages/CryptoCom.Net" target="_blank"><img src="https://img.shields.io/nuget/v/CryptoCom.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>Gate.io</td><td><a href="https://github.com/JKorf/GateIo.Net">JKorf/GateIo.Net</a></td><td><a href="https://www.nuget.org/packages/GateIo.Net" target="_blank"><img src="https://img.shields.io/nuget/v/GateIo.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>Gate.io</td><td><a href="https://github.com/JKorf/GateIo.Net">JKorf/GateIo.Net</a></td><td><a href="https://www.nuget.org/packages/GateIo.Net" target="_blank"><img src="https://img.shields.io/nuget/v/GateIo.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>HTX</td><td><a href="https://github.com/JKorf/HTX.Net">JKorf/HTX.Net</a></td><td><a href="https://www.nuget.org/packages/JKorf.HTX.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JKorf.HTX.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>HTX</td><td><a href="https://github.com/JKorf/HTX.Net">JKorf/HTX.Net</a></td><td><a href="https://www.nuget.org/packages/JKorf.HTX.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JKorf.HTX.net.svg?style=flat-square" /></a></td></tr>
|
||||||
|
<tr><td>HyperLiquid</td><td><a href="https://github.com/JKorf/HyperLiquid.Net">JKorf/HyperLiquid.Net</a></td><td><a href="https://www.nuget.org/packages/HyperLiquid.Net" target="_blank"><img src="https://img.shields.io/nuget/v/HyperLiquid.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>Kraken</td><td><a href="https://github.com/JKorf/Kraken.Net">JKorf/Kraken.Net</a></td><td><a href="https://www.nuget.org/packages/KrakenExchange.Net" target="_blank"><img src="https://img.shields.io/nuget/v/KrakenExchange.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>Kraken</td><td><a href="https://github.com/JKorf/Kraken.Net">JKorf/Kraken.Net</a></td><td><a href="https://www.nuget.org/packages/KrakenExchange.Net" target="_blank"><img src="https://img.shields.io/nuget/v/KrakenExchange.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>Kucoin</td><td><a href="https://github.com/JKorf/Kucoin.Net">JKorf/Kucoin.Net</a></td><td><a href="https://www.nuget.org/packages/Kucoin.Net" target="_blank"><img src="https://img.shields.io/nuget/v/Kucoin.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>Kucoin</td><td><a href="https://github.com/JKorf/Kucoin.Net">JKorf/Kucoin.Net</a></td><td><a href="https://www.nuget.org/packages/Kucoin.Net" target="_blank"><img src="https://img.shields.io/nuget/v/Kucoin.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>Mexc</td><td><a href="https://github.com/JKorf/Mexc.Net">JKorf/Mexc.Net</a></td><td><a href="https://www.nuget.org/packages/JK.Mexc.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JK.Mexc.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>Mexc</td><td><a href="https://github.com/JKorf/Mexc.Net">JKorf/Mexc.Net</a></td><td><a href="https://www.nuget.org/packages/JK.Mexc.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JK.Mexc.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>OKX</td><td><a href="https://github.com/JKorf/OKX.Net">JKorf/OKX.Net</a></td><td><a href="https://www.nuget.org/packages/JK.OKX.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JK.OKX.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>OKX</td><td><a href="https://github.com/JKorf/OKX.Net">JKorf/OKX.Net</a></td><td><a href="https://www.nuget.org/packages/JK.OKX.Net" target="_blank"><img src="https://img.shields.io/nuget/v/JK.OKX.net.svg?style=flat-square" /></a></td></tr>
|
||||||
<tr><td>WhiteBit</td><td><a href="https://github.com/JKorf/WhiteBit.Net">JKorf/WhiteBit.Net</a></td><td><a href="https://www.nuget.org/packages/WhiteBit.Net" target="_blank"><img src="https://img.shields.io/nuget/v/WhiteBit.net.svg?style=flat-square" /></a></td></tr>
|
<tr><td>WhiteBit</td><td><a href="https://github.com/JKorf/WhiteBit.Net">JKorf/WhiteBit.Net</a></td><td><a href="https://www.nuget.org/packages/WhiteBit.Net" target="_blank"><img src="https://img.shields.io/nuget/v/WhiteBit.net.svg?style=flat-square" /></a></td></tr>
|
||||||
|
<tr><td>XT</td><td><a href="https://github.com/JKorf/XT.Net">JKorf/XT.Net</a></td><td><a href="https://www.nuget.org/packages/XT.Net" target="_blank"><img src="https://img.shields.io/nuget/v/XT.net.svg?style=flat-square" /></a></td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p>Note that there are 3rd party implementations going around, but only the listed ones here are created and supported by me.</p>
|
<p>Note that there are 3rd party implementations going around, but only the listed ones here are created and supported by me.</p>
|
||||||
<p>When using multiple of these API's the <a href="https://github.com/jkorf/CryptoClients.Net">CryptoClients.Net</a> package can be used which combines these packages and allows easy access to all exchange API's.</p>
|
<p>When using multiple of these API's the <a href="https://github.com/jkorf/CryptoClients.Net">CryptoClients.Net</a> package can be used which combines these packages and allows easy access to all exchange API's.</p>
|
||||||
@@ -194,6 +196,22 @@
|
|||||||
|
|
||||||
<h4>Support the project</h4>
|
<h4>Support the project</h4>
|
||||||
|
|
||||||
|
<b>Referral</b>
|
||||||
|
<p>When creating an account on new exchanges please consider using a referral link from below to support development</p>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tr><th>Exchange</th><th>Link</th></tr>
|
||||||
|
<tr><td>Bybit</td><td>https://partner.bybit.com/b/jkorf</td></tr>
|
||||||
|
<tr><td>Coinbase</td><td>https://advanced.coinbase.com/join/T6H54H8</td></tr>
|
||||||
|
<tr><td>CoinEx</td><td>https://www.coinex.com/register?refer_code=hd6gn</td></tr>
|
||||||
|
<tr><td>Crypto.com</td><td>https://crypto.com/exch/26ge92xbkn</td></tr>
|
||||||
|
<tr><td>HTX</td><td>https://www.htx.com/invite/en-us/1f?invite_code=fxp9</td></tr>
|
||||||
|
<tr><td>HyperLiquid</td><td>https://app.hyperliquid.xyz/join/JKORF</td></tr>
|
||||||
|
<tr><td>Kucoin</td><td>https://www.kucoin.com/r/rf/QBS4FPED</td></tr>
|
||||||
|
<tr><td>OKX</td><td>https://okx.com/join/48046699</td></tr>
|
||||||
|
<tr><td>WhiteBit</td><td>https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf</td></tr>
|
||||||
|
<tr><td>XT</td><td>https://www.xt.com/en/accounts/register?ref=1HRM5J</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<b>Donate</b><br />
|
<b>Donate</b><br />
|
||||||
<p>Make a one time donation in a crypto currency of your choice. If you prefer to donate a currency not listed here please contact me.<p>
|
<p>Make a one time donation in a crypto currency of your choice. If you prefer to donate a currency not listed here please contact me.<p>
|
||||||
|
|
||||||
@@ -203,9 +221,7 @@
|
|||||||
|
|
||||||
<b>Sponsor</b><br />
|
<b>Sponsor</b><br />
|
||||||
<p>Alternatively, sponsor me on Github using <a href="https://github.com/sponsors/JKorf">Github Sponsors</a>.</p>
|
<p>Alternatively, sponsor me on Github using <a href="https://github.com/sponsors/JKorf">Github Sponsors</a>.</p>
|
||||||
|
|
||||||
<div class="alert alert-info">I develop and maintain these packages on my own for free in my spare time, any support is greatly appreciated.</div>
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<hr class="divider">
|
<hr class="divider">
|
||||||
@@ -266,6 +282,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="install-htx-tab" data-toggle="tab" href="#install-htx" role="tab" aria-controls="install-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="install-htx-tab" data-toggle="tab" href="#install-htx" role="tab" aria-controls="install-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="install-hyperliquid-tab" data-toggle="tab" href="#install-hyperliquid" role="tab" aria-controls="install-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="install-kraken-tab" data-toggle="tab" href="#install-kraken" role="tab" aria-controls="install-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="install-kraken-tab" data-toggle="tab" href="#install-kraken" role="tab" aria-controls="install-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -279,7 +298,10 @@
|
|||||||
<a class="nav-link" id="install-okx-tab" data-toggle="tab" href="#install-okx" role="tab" aria-controls="install-okx" aria-selected="false">OKX</a>
|
<a class="nav-link" id="install-okx-tab" data-toggle="tab" href="#install-okx" role="tab" aria-controls="install-okx" aria-selected="false">OKX</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="install-whitebit-tab" data-toggle="tab" href="#install-whitebit" role="tab" aria-controls="install-whitebit" aria-selected="false">OKX</a>
|
<a class="nav-link" id="install-whitebit-tab" data-toggle="tab" href="#install-whitebit" role="tab" aria-controls="install-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="install-xt-tab" data-toggle="tab" href="#install-xt" role="tab" aria-controls="install-xt" aria-selected="false">XT</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
@@ -328,6 +350,9 @@
|
|||||||
<div class="tab-pane fade" id="install-htx" role="tabpanel" aria-labelledby="install-htx-tab">
|
<div class="tab-pane fade" id="install-htx" role="tabpanel" aria-labelledby="install-htx-tab">
|
||||||
<pre><code>dotnet add package JKorf.HTX.Net</code></pre>
|
<pre><code>dotnet add package JKorf.HTX.Net</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="install-hyperliquid" role="tabpanel" aria-labelledby="install-hyperliquid-tab">
|
||||||
|
<pre><code>dotnet add package HyperLiquid.Net</code></pre>
|
||||||
|
</div>
|
||||||
<div class="tab-pane fade" id="install-kraken" role="tabpanel" aria-labelledby="install-kraken-tab">
|
<div class="tab-pane fade" id="install-kraken" role="tabpanel" aria-labelledby="install-kraken-tab">
|
||||||
<pre><code>dotnet add package KrakenExchange.Net</code></pre>
|
<pre><code>dotnet add package KrakenExchange.Net</code></pre>
|
||||||
<img src="assets/images/KrakenInstall.png" />
|
<img src="assets/images/KrakenInstall.png" />
|
||||||
@@ -347,6 +372,9 @@
|
|||||||
<div class="tab-pane fade" id="install-whitebit" role="tabpanel" aria-labelledby="install-whitebit-tab">
|
<div class="tab-pane fade" id="install-whitebit" role="tabpanel" aria-labelledby="install-whitebit-tab">
|
||||||
<pre><code>dotnet add package WhiteBit.Net</code></pre>
|
<pre><code>dotnet add package WhiteBit.Net</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="install-xt" role="tabpanel" aria-labelledby="install-xt-tab">
|
||||||
|
<pre><code>dotnet add package XT.Net</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -400,6 +428,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="di-htx-tab" data-toggle="tab" href="#di-htx" role="tab" aria-controls="di-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="di-htx-tab" data-toggle="tab" href="#di-htx" role="tab" aria-controls="di-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="di-hyperliquid-tab" data-toggle="tab" href="#di-hyperliquid" role="tab" aria-controls="di-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="di-kraken-tab" data-toggle="tab" href="#di-kraken" role="tab" aria-controls="di-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="di-kraken-tab" data-toggle="tab" href="#di-kraken" role="tab" aria-controls="di-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -415,6 +446,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="di-whitebit-tab" data-toggle="tab" href="#di-whitebit" role="tab" aria-controls="di-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="di-whitebit-tab" data-toggle="tab" href="#di-whitebit" role="tab" aria-controls="di-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="di-xt-tab" data-toggle="tab" href="#di-xt" role="tab" aria-controls="di-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="di-cc" role="tabpanel" aria-labelledby="di-cc-tab">
|
<div class="tab-pane fade show active" id="di-cc" role="tabpanel" aria-labelledby="di-cc-tab">
|
||||||
@@ -455,6 +489,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="di-htx" role="tabpanel" aria-labelledby="di-htx-tab">
|
<div class="tab-pane fade" id="di-htx" role="tabpanel" aria-labelledby="di-htx-tab">
|
||||||
<pre><code>builder.Services.AddHTX();</code></pre>
|
<pre><code>builder.Services.AddHTX();</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="di-hyperliquid" role="tabpanel" aria-labelledby="di-hyperliquid-tab">
|
||||||
|
<pre><code>builder.Services.AddHyperLiquid();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="di-kraken" role="tabpanel" aria-labelledby="di-kraken-tab">
|
<div class="tab-pane fade" id="di-kraken" role="tabpanel" aria-labelledby="di-kraken-tab">
|
||||||
<pre><code>builder.Services.AddKraken();</code></pre>
|
<pre><code>builder.Services.AddKraken();</code></pre>
|
||||||
@@ -471,6 +508,9 @@
|
|||||||
<div class="tab-pane fade" id="di-whitebit" role="tabpanel" aria-labelledby="di-whitebit-tab">
|
<div class="tab-pane fade" id="di-whitebit" role="tabpanel" aria-labelledby="di-whitebit-tab">
|
||||||
<pre><code>builder.Services.AddWhiteBit();</code></pre>
|
<pre><code>builder.Services.AddWhiteBit();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="di-xt" role="tabpanel" aria-labelledby="di-xt-tab">
|
||||||
|
<pre><code>builder.Services.AddXT();</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -516,6 +556,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="interfaces-htx-tab" data-toggle="tab" href="#interfaces-htx" role="tab" aria-controls="interfaces-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="interfaces-htx-tab" data-toggle="tab" href="#interfaces-htx" role="tab" aria-controls="interfaces-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="interfaces-hyperliquid-tab" data-toggle="tab" href="#interfaces-hyperliquid" role="tab" aria-controls="interfaces-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="interfaces-kraken-tab" data-toggle="tab" href="#interfaces-kraken" role="tab" aria-controls="interfaces-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="interfaces-kraken-tab" data-toggle="tab" href="#interfaces-kraken" role="tab" aria-controls="interfaces-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -531,6 +574,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="interfaces-whitebit-tab" data-toggle="tab" href="#interfaces-whitebit" role="tab" aria-controls="interfaces-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="interfaces-whitebit-tab" data-toggle="tab" href="#interfaces-whitebit" role="tab" aria-controls="interfaces-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="interfaces-xt-tab" data-toggle="tab" href="#interfaces-xt" role="tab" aria-controls="interfaces-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="interfaces-cc" role="tabpanel" aria-labelledby="interfaces-cc-tab">
|
<div class="tab-pane fade show active" id="interfaces-cc" role="tabpanel" aria-labelledby="interfaces-cc-tab">
|
||||||
@@ -946,6 +992,39 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="interfaces-hyperliquid" role="tabpanel" aria-labelledby="interfaces-hyperliquid-tab">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tr><th>Interface</th><th>Description</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IHyperLiquidRestClient</code></td>
|
||||||
|
<td>The client for accessing the HyperLiquid REST API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IHyperLiquidSocketClient</code></td>
|
||||||
|
<td>The client for accessing the HyperLiquid Websocket API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IHyperLiquidOrderBookFactory</code></td>
|
||||||
|
<td>A factory for creating SymbolOrderBook instances for the HyperLiquid API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IHyperLiquidTrackerFactory</code></td>
|
||||||
|
<td>A factory for creating kline and trade Tracker instances for the HyperLiquid API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ICryptoRestClient</code></td>
|
||||||
|
<td>An aggregating client from which multiple different library REST clients can be accessed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ICryptoSocketClient</code></td>
|
||||||
|
<td>An aggregating client from which multiple different library Websocket clients can be accessed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ISharedClient</code></td>
|
||||||
|
<td>Various interfaces deriving from ISharedClient which can be used for common functionality</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div class="tab-pane fade" id="interfaces-kraken" role="tabpanel" aria-labelledby="interfaces-kraken-tab">
|
<div class="tab-pane fade" id="interfaces-kraken" role="tabpanel" aria-labelledby="interfaces-kraken-tab">
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<tr><th>Interface</th><th>Description</th></tr>
|
<tr><th>Interface</th><th>Description</th></tr>
|
||||||
@@ -1111,6 +1190,39 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="interfaces-xt" role="tabpanel" aria-labelledby="interfaces-xt-tab">
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tr><th>Interface</th><th>Description</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IXTRestClient</code></td>
|
||||||
|
<td>The client for accessing the XT REST API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IXTSocketClient</code></td>
|
||||||
|
<td>The client for accessing the XT Websocket API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IXTOrderBookFactory</code></td>
|
||||||
|
<td>A factory for creating SymbolOrderBook instances for the XT API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>IXTTrackerFactory</code></td>
|
||||||
|
<td>A factory for creating kline and trade Tracker instances for the XT API</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ICryptoRestClient</code></td>
|
||||||
|
<td>An aggregating client from which multiple different library REST clients can be accessed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ICryptoSocketClient</code></td>
|
||||||
|
<td>An aggregating client from which multiple different library Websocket clients can be accessed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ISharedClient</code></td>
|
||||||
|
<td>Various interfaces deriving from ISharedClient which can be used for common functionality</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -1122,7 +1234,7 @@
|
|||||||
<p>All clients work with the same principles:</p>
|
<p>All clients work with the same principles:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Mandatory parameters are non-nullable while optional parameters are nullable and will have a default value of null.</li>
|
<li>Mandatory parameters are non-nullable while optional parameters are nullable and will have a default value of null.</li>
|
||||||
<li>Any operation will return a form of <code>CallResult</code>. This result can and should be checked for success using the `Success` property. If `Success` is false the `Error` property will have more info.</li>
|
<li>Any operation will return a form of <code>CallResult</code>. This result can and should be checked for success using the <code>Success</code> property. If <code>Success</code> is false the <code>Error</code> property will have more info.</li>
|
||||||
<li>Clients will not throw exceptions.</li>
|
<li>Clients will not throw exceptions.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
@@ -1175,6 +1287,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="rest-htx-tab" data-toggle="tab" href="#rest-htx" role="tab" aria-controls="rest-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="rest-htx-tab" data-toggle="tab" href="#rest-htx" role="tab" aria-controls="rest-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="rest-hyperliquid-tab" data-toggle="tab" href="#rest-hyperliquid" role="tab" aria-controls="rest-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="rest-kraken-tab" data-toggle="tab" href="#rest-kraken" role="tab" aria-controls="rest-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="rest-kraken-tab" data-toggle="tab" href="#rest-kraken" role="tab" aria-controls="rest-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1190,6 +1305,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="rest-whitebit-tab" data-toggle="tab" href="#rest-whitebit" role="tab" aria-controls="rest-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="rest-whitebit-tab" data-toggle="tab" href="#rest-whitebit" role="tab" aria-controls="rest-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="rest-xt-tab" data-toggle="tab" href="#rest-xt" role="tab" aria-controls="rest-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="rest-cc" role="tabpanel" aria-labelledby="rest-cc-tab">
|
<div class="tab-pane fade show active" id="rest-cc" role="tabpanel" aria-labelledby="rest-cc-tab">
|
||||||
@@ -1344,6 +1462,18 @@ if (!tickersResult.Success)
|
|||||||
// Handle error, tickersResult.Error contains more information
|
// Handle error, tickersResult.Error contains more information
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// Handle data, tickersResult.Data will contain the actual data
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="rest-hyperliquid" role="tabpanel" aria-labelledby="rest-hyperliquid-tab">
|
||||||
|
<pre><code>var client = new HyperLiquidRestClient();
|
||||||
|
var tickersResult = await client.SpotApi.ExchangeData.GetExchangeInfoAndTickersAsync();
|
||||||
|
if (!tickersResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, tickersResult.Error contains more information
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Handle data, tickersResult.Data will contain the actual data
|
// Handle data, tickersResult.Data will contain the actual data
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
@@ -1404,6 +1534,18 @@ if (!tickersResult.Success)
|
|||||||
// Handle error, tickersResult.Error contains more information
|
// Handle error, tickersResult.Error contains more information
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// Handle data, tickersResult.Data will contain the actual data
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="rest-xt" role="tabpanel" aria-labelledby="rest-xt-tab">
|
||||||
|
<pre><code>var client = new XTRestClient();
|
||||||
|
var tickersResult = await client.SpotApi.ExchangeData.GetTickersAsync();
|
||||||
|
if (!tickersResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, tickersResult.Error contains more information
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Handle data, tickersResult.Data will contain the actual data
|
// Handle data, tickersResult.Data will contain the actual data
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
@@ -1518,6 +1660,9 @@ else
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="socket-htx-tab" data-toggle="tab" href="#socket-htx" role="tab" aria-controls="socket-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="socket-htx-tab" data-toggle="tab" href="#socket-htx" role="tab" aria-controls="socket-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="socket-hyperliquid-tab" data-toggle="tab" href="#socket-hyperliquid" role="tab" aria-controls="socket-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="socket-kraken-tab" data-toggle="tab" href="#socket-kraken" role="tab" aria-controls="socket-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="socket-kraken-tab" data-toggle="tab" href="#socket-kraken" role="tab" aria-controls="socket-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1533,6 +1678,9 @@ else
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="socket-whitebit-tab" data-toggle="tab" href="#socket-whitebit" role="tab" aria-controls="socket-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="socket-whitebit-tab" data-toggle="tab" href="#socket-whitebit" role="tab" aria-controls="socket-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="socket-xt-tab" data-toggle="tab" href="#socket-xt" role="tab" aria-controls="socket-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="socket-cc" role="tabpanel" aria-labelledby="socket-cc-tab">
|
<div class="tab-pane fade show active" id="socket-cc" role="tabpanel" aria-labelledby="socket-cc-tab">
|
||||||
@@ -1665,6 +1813,17 @@ if (!subscribeResult.Success)
|
|||||||
{
|
{
|
||||||
// Handle error, subscribeResult.Error contains more information on why the subscription failed
|
// Handle error, subscribeResult.Error contains more information on why the subscription failed
|
||||||
}
|
}
|
||||||
|
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="socket-hyperliquid" role="tabpanel" aria-labelledby="socket-hyperliquid-tab">
|
||||||
|
<pre><code>var client = new HyperLiquidSocketClient();
|
||||||
|
var subscribeResult = await client.SpotApi.SubscribeToSymbolUpdatesAsync("HYPE/USDC", update => {
|
||||||
|
// Handle the data update, update.Data will contain the actual data
|
||||||
|
});
|
||||||
|
if (!subscribeResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, subscribeResult.Error contains more information on why the subscription failed
|
||||||
|
}
|
||||||
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="socket-kraken" role="tabpanel" aria-labelledby="socket-kraken-tab">
|
<div class="tab-pane fade" id="socket-kraken" role="tabpanel" aria-labelledby="socket-kraken-tab">
|
||||||
@@ -1711,7 +1870,7 @@ if (!subscribeResult.Success)
|
|||||||
}
|
}
|
||||||
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="socket-okx" role="tabpanel" aria-labelledby="socket-okx-tab">
|
<div class="tab-pane fade" id="socket-whitebit" role="tabpanel" aria-labelledby="socket-whitebit-tab">
|
||||||
<pre><code>var client = new WhiteBitSocketClient();
|
<pre><code>var client = new WhiteBitSocketClient();
|
||||||
var subscribeResult = await client.V4Api.ExchangeData.SubscribeToTickerUpdatesAsync("ETH_USDT", update => {
|
var subscribeResult = await client.V4Api.ExchangeData.SubscribeToTickerUpdatesAsync("ETH_USDT", update => {
|
||||||
// Handle the data update, update.Data will contain the actual data
|
// Handle the data update, update.Data will contain the actual data
|
||||||
@@ -1722,6 +1881,18 @@ if (!subscribeResult.Success)
|
|||||||
}
|
}
|
||||||
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="socket-xt" role="tabpanel" aria-labelledby="socket-xt-tab">
|
||||||
|
<pre><code>var client = new XTSocketClient();
|
||||||
|
var subscribeResult = await client.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("eth_usdt", update => {
|
||||||
|
// Handle the data update, update.Data will contain the actual data
|
||||||
|
});
|
||||||
|
if (!subscribeResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, subscribeResult.Error contains more information on why the subscription failed
|
||||||
|
}
|
||||||
|
// Subscribing was successfull, the data will now be streamed into the data handler</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1907,6 +2078,9 @@ var binanceTriggered = CheckForTrigger(lastBinanceTicker);</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="shared-htx-tab" data-toggle="tab" href="#shared-htx" role="tab" aria-controls="shared-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="shared-htx-tab" data-toggle="tab" href="#shared-htx" role="tab" aria-controls="shared-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="shared-hyperliquid-tab" data-toggle="tab" href="#shared-hyperliquid" role="tab" aria-controls="shared-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="shared-kraken-tab" data-toggle="tab" href="#shared-kraken" role="tab" aria-controls="shared-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="shared-kraken-tab" data-toggle="tab" href="#shared-kraken" role="tab" aria-controls="shared-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -1922,6 +2096,9 @@ var binanceTriggered = CheckForTrigger(lastBinanceTicker);</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="shared-whitebit-tab" data-toggle="tab" href="#shared-whitebit" role="tab" aria-controls="shared-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="shared-whitebit-tab" data-toggle="tab" href="#shared-whitebit" role="tab" aria-controls="shared-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="shared-xt-tab" data-toggle="tab" href="#shared-xt" role="tab" aria-controls="shared-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="shared-binance" role="tabpanel" aria-labelledby="shared-binance-tab">
|
<div class="tab-pane fade show active" id="shared-binance" role="tabpanel" aria-labelledby="shared-binance-tab">
|
||||||
@@ -2057,6 +2234,19 @@ var usdFuturesSharedRestClient = htxRestClient.UsdtFuturesApi.SharedClient;
|
|||||||
|
|
||||||
// USDT Futures API common functionality socket client
|
// USDT Futures API common functionality socket client
|
||||||
var usdFuturesSharedSocketClient = htxSocketClient.UsdtFuturesApi.SharedClient;</code></pre>
|
var usdFuturesSharedSocketClient = htxSocketClient.UsdtFuturesApi.SharedClient;</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="shared-hyperliquid" role="tabpanel" aria-labelledby="shared-hyperliquid-tab">
|
||||||
|
<pre><code>// Spot API common functionality rest client
|
||||||
|
var spotSharedRestClients = hyperliquidRestClient.SpotApi.SharedClient;
|
||||||
|
|
||||||
|
// Spot API common functionality socket client
|
||||||
|
var spotSharedSocketClient = hyperliquidSocketClient.SpotApi.SharedClient;
|
||||||
|
|
||||||
|
// Perpetual Futures API common functionality rest client
|
||||||
|
var futuresSharedRestClient = hyperliquidRestClient.FuturesApi.SharedClient;
|
||||||
|
|
||||||
|
// Perpetual Futures API common functionality socket client
|
||||||
|
var futuresSharedSocketClient = hyperliquidSocketClient.FuturesApi.SharedClient;</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="shared-kraken" role="tabpanel" aria-labelledby="shared-kraken-tab">
|
<div class="tab-pane fade" id="shared-kraken" role="tabpanel" aria-labelledby="shared-kraken-tab">
|
||||||
<pre><code>// Spot API common functionality rest client
|
<pre><code>// Spot API common functionality rest client
|
||||||
@@ -2105,6 +2295,19 @@ var spotSharedRestClients = whitebitRestClient.V4Api.SharedClient;
|
|||||||
// Futures and Spot API common functionality socket client
|
// Futures and Spot API common functionality socket client
|
||||||
var spotSharedSocketClient = whitebitSocketClient.V4Api.SharedClient;</code></pre>
|
var spotSharedSocketClient = whitebitSocketClient.V4Api.SharedClient;</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="shared-xt" role="tabpanel" aria-labelledby="shared-xt-tab">
|
||||||
|
<pre><code>// Spot API common functionality rest client
|
||||||
|
var spotSharedRestClients = xtRestClient.SpotApi.SharedClient;
|
||||||
|
|
||||||
|
// Futures API common functionality rest client
|
||||||
|
var futuresSharedRestClients = xtRestClient.UsdtFuturesApi.SharedClient;
|
||||||
|
|
||||||
|
// Spot API common functionality socket client
|
||||||
|
var spotSharedSocketClient = xtSocketClient.SpotApi.SharedClient;
|
||||||
|
|
||||||
|
// Futures API common functionality socket client
|
||||||
|
var futuresSharedSocketClient = xtSocketClient.FuturesApi.SharedClient;</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 id="shared_tradingmode">TradingMode</h4>
|
<h4 id="shared_tradingmode">TradingMode</h4>
|
||||||
@@ -2232,6 +2435,7 @@ var balances = await restClient.HTX.SpotApi.SharedClient.GetBalancesAsync(new Ge
|
|||||||
<tr><td><code>ILeverageRestClient</code></td><td>For managing leverage for a Futures symbol</td></tr>
|
<tr><td><code>ILeverageRestClient</code></td><td>For managing leverage for a Futures symbol</td></tr>
|
||||||
<tr><td><code>IPositionHistoryRestClient</code></td><td>For requesting the user position closing history</td></tr>
|
<tr><td><code>IPositionHistoryRestClient</code></td><td>For requesting the user position closing history</td></tr>
|
||||||
<tr><td><code>IPositionModeRestClient</code></td><td>For managing the position mode for the user</td></tr>
|
<tr><td><code>IPositionModeRestClient</code></td><td>For managing the position mode for the user</td></tr>
|
||||||
|
<tr><td><code>IFeeRestClient</code></td><td>For requesting maker and taker trading fee percentages for the user</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p style="font-style: italic;">Available Socket shared interfaces</p>
|
<p style="font-style: italic;">Available Socket shared interfaces</p>
|
||||||
@@ -2376,6 +2580,9 @@ options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-htx" role="tab" aria-controls="options-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-htx" role="tab" aria-controls="options-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-hyperliquid-tab" data-toggle="tab" href="#options-hyperliquid" role="tab" aria-controls="options-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-kraken" role="tab" aria-controls="options-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-kraken" role="tab" aria-controls="options-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -2391,6 +2598,9 @@ options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-whitebit" role="tab" aria-controls="options-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-whitebit" role="tab" aria-controls="options-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-xt-tab" data-toggle="tab" href="#options-xt" role="tab" aria-controls="options-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="options-cc" role="tabpanel" aria-labelledby="options-cc-tab">
|
<div class="tab-pane fade show active" id="options-cc" role="tabpanel" aria-labelledby="options-cc-tab">
|
||||||
@@ -2544,6 +2754,18 @@ builder.Services.AddGateIo(builder.Configuration.GetSection("GateIo"));</code></
|
|||||||
|
|
||||||
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
||||||
builder.Services.AddHTX(builder.Configuration.GetSection("HTX"));</code></pre>
|
builder.Services.AddHTX(builder.Configuration.GetSection("HTX"));</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-hyperliquid" role="tabpanel" aria-labelledby="options-hyperliquid-tab">
|
||||||
|
<pre><code>builder.Services.AddHyperLiquid(
|
||||||
|
options => {
|
||||||
|
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// OR
|
||||||
|
|
||||||
|
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
||||||
|
builder.Services.AddHyperLiquid(builder.Configuration.GetSection("HyperLiquid"));</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="options-kraken" role="tabpanel" aria-labelledby="options-kraken-tab">
|
<div class="tab-pane fade" id="options-kraken" role="tabpanel" aria-labelledby="options-kraken-tab">
|
||||||
<pre><code>builder.Services.AddKraken(
|
<pre><code>builder.Services.AddKraken(
|
||||||
@@ -2605,6 +2827,18 @@ builder.Services.AddOKX(builder.Configuration.GetSection("OKX"));</code></pre>
|
|||||||
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
||||||
builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</code></pre>
|
builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-xt" role="tabpanel" aria-labelledby="options-xt-tab">
|
||||||
|
<pre><code>builder.Services.AddXT(
|
||||||
|
options => {
|
||||||
|
options.Rest.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
options.Socket.RequestTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// OR
|
||||||
|
|
||||||
|
// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration
|
||||||
|
builder.Services.AddXT(builder.Configuration.GetSection("XT"));</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2651,6 +2885,9 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-constr-htx" role="tab" aria-controls="options-constr-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-constr-htx" role="tab" aria-controls="options-constr-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-hyperliquid-tab" data-toggle="tab" href="#options-constr-hyperliquid" role="tab" aria-controls="options-constr-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-constr-kraken" role="tab" aria-controls="options-constr-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-constr-kraken" role="tab" aria-controls="options-constr-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -2666,6 +2903,9 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-constr-whitebit" role="tab" aria-controls="options-constr-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-constr-whitebit" role="tab" aria-controls="options-constr-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-xt-tab" data-toggle="tab" href="#options-constr-xt" role="tab" aria-controls="options-constr-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="options-constr-cc" role="tabpanel" aria-labelledby="options-cc-tab">
|
<div class="tab-pane fade show active" id="options-constr-cc" role="tabpanel" aria-labelledby="options-cc-tab">
|
||||||
@@ -2742,6 +2982,12 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="options-constr-htx" role="tabpanel" aria-labelledby="options-htx-tab">
|
<div class="tab-pane fade" id="options-constr-htx" role="tabpanel" aria-labelledby="options-htx-tab">
|
||||||
<pre><code>var client = new HTXRestClient(opts =>
|
<pre><code>var client = new HTXRestClient(opts =>
|
||||||
|
{
|
||||||
|
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
});</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-constr-hyperliquid" role="tabpanel" aria-labelledby="options-hyperliquid-tab">
|
||||||
|
<pre><code>var client = new HyperLiquidRestClient(opts =>
|
||||||
{
|
{
|
||||||
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
});</code></pre>
|
});</code></pre>
|
||||||
@@ -2772,6 +3018,12 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="options-constr-whitebit" role="tabpanel" aria-labelledby="options-whitebit-tab">
|
<div class="tab-pane fade" id="options-constr-whitebit" role="tabpanel" aria-labelledby="options-whitebit-tab">
|
||||||
<pre><code>var client = new WhiteBitRestClient(opts =>
|
<pre><code>var client = new WhiteBitRestClient(opts =>
|
||||||
|
{
|
||||||
|
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
});</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-constr-xt" role="tabpanel" aria-labelledby="options-xt-tab">
|
||||||
|
<pre><code>var client = new XTRestClient(opts =>
|
||||||
{
|
{
|
||||||
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
opts.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
});</code></pre>
|
});</code></pre>
|
||||||
@@ -2819,6 +3071,9 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-default-htx" role="tab" aria-controls="options-default-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="options-htx-tab" data-toggle="tab" href="#options-default-htx" role="tab" aria-controls="options-default-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-hyperliquid-tab" data-toggle="tab" href="#options-default-hyperliquid" role="tab" aria-controls="options-default-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-default-kraken" role="tab" aria-controls="options-default-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="options-kraken-tab" data-toggle="tab" href="#options-default-kraken" role="tab" aria-controls="options-default-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -2834,6 +3089,9 @@ builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));</cod
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-default-whitebit" role="tab" aria-controls="options-default-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="options-whitebit-tab" data-toggle="tab" href="#options-default-whitebit" role="tab" aria-controls="options-default-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="options-xt-tab" data-toggle="tab" href="#options-default-xt" role="tab" aria-controls="options-default-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="options-default-binance" role="tabpanel" aria-labelledby="options-binance-tab">
|
<div class="tab-pane fade show active" id="options-default-binance" role="tabpanel" aria-labelledby="options-binance-tab">
|
||||||
@@ -2919,6 +3177,13 @@ var client = new GateIoRestClient();</code></pre>
|
|||||||
options.RequestTimeout = TimeSpan.FromSeconds(30);
|
options.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
});
|
});
|
||||||
var client = new HTXRestClient();</code></pre>
|
var client = new HTXRestClient();</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-default-hyperliquid" role="tabpanel" aria-labelledby="options-hyperliquid-tab">
|
||||||
|
<pre><code>HyperLiquidRestClient.SetDefaultOptions(options =>
|
||||||
|
{
|
||||||
|
options.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
});
|
||||||
|
var client = new HyperLiquidRestClient();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="options-default-kraken" role="tabpanel" aria-labelledby="options-kraken-tab">
|
<div class="tab-pane fade" id="options-default-kraken" role="tabpanel" aria-labelledby="options-kraken-tab">
|
||||||
<pre><code>KrakenRestClient.SetDefaultOptions(options =>
|
<pre><code>KrakenRestClient.SetDefaultOptions(options =>
|
||||||
@@ -2955,6 +3220,13 @@ var client = new OKXRestClient();</code></pre>
|
|||||||
});
|
});
|
||||||
var client = new WhiteBitRestClient();</code></pre>
|
var client = new WhiteBitRestClient();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="options-default-xt" role="tabpanel" aria-labelledby="options-xt-tab">
|
||||||
|
<pre><code>XTRestClient.SetDefaultOptions(options =>
|
||||||
|
{
|
||||||
|
options.RequestTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
});
|
||||||
|
var client = new XTRestClient();</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -3174,6 +3446,9 @@ var client = new WhiteBitRestClient();</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="book-htx-tab" data-toggle="tab" href="#book-htx" role="tab" aria-controls="book-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="book-htx-tab" data-toggle="tab" href="#book-htx" role="tab" aria-controls="book-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="book-hyperliquid-tab" data-toggle="tab" href="#book-hyperliquid" role="tab" aria-controls="book-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="book-kraken-tab" data-toggle="tab" href="#book-kraken" role="tab" aria-controls="book-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="book-kraken-tab" data-toggle="tab" href="#book-kraken" role="tab" aria-controls="book-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -3189,6 +3464,9 @@ var client = new WhiteBitRestClient();</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="book-whitebit-tab" data-toggle="tab" href="#book-whitebit" role="tab" aria-controls="book-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="book-whitebit-tab" data-toggle="tab" href="#book-whitebit" role="tab" aria-controls="book-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="book-xt-tab" data-toggle="tab" href="#book-xt" role="tab" aria-controls="book-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="book-cryptoclients" role="tabpanel" aria-labelledby="book-cryptoclients-tab">
|
<div class="tab-pane fade show active" id="book-cryptoclients" role="tabpanel" aria-labelledby="book-cryptoclients-tab">
|
||||||
@@ -3344,6 +3622,19 @@ if (!startResult.Success)
|
|||||||
}
|
}
|
||||||
// Book has successfully started and synchronized
|
// Book has successfully started and synchronized
|
||||||
|
|
||||||
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
|
await book.StopAsync();
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="book-hyperliquid" role="tabpanel" aria-labelledby="book-hyperliquid-tab">
|
||||||
|
<pre><code>var book = new HyperLiquidSymbolOrderBook("HYPE/USDC");
|
||||||
|
var startResult = await book.StartAsync();
|
||||||
|
if (!startResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, error info available in startResult.Error
|
||||||
|
}
|
||||||
|
// Book has successfully started and synchronized
|
||||||
|
|
||||||
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
await book.StopAsync();
|
await book.StopAsync();
|
||||||
</code></pre>
|
</code></pre>
|
||||||
@@ -3409,6 +3700,19 @@ if (!startResult.Success)
|
|||||||
}
|
}
|
||||||
// Book has successfully started and synchronized
|
// Book has successfully started and synchronized
|
||||||
|
|
||||||
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
|
await book.StopAsync();
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="book-xt" role="tabpanel" aria-labelledby="book-xt-tab">
|
||||||
|
<pre><code>var book = new XTSymbolOrderBook("eth_usdt");
|
||||||
|
var startResult = await book.StartAsync();
|
||||||
|
if (!startResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, error info available in startResult.Error
|
||||||
|
}
|
||||||
|
// Book has successfully started and synchronized
|
||||||
|
|
||||||
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
await book.StopAsync();
|
await book.StopAsync();
|
||||||
</code></pre>
|
</code></pre>
|
||||||
@@ -3567,6 +3871,9 @@ foreach (var book in books.Where(b => b.Status == OrderBookStatus.Synced))
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="tracker-htx-tab" data-toggle="tab" href="#tracker-htx" role="tab" aria-controls="tracker-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="tracker-htx-tab" data-toggle="tab" href="#tracker-htx" role="tab" aria-controls="tracker-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="tracker-hyperliquid-tab" data-toggle="tab" href="#tracker-hyperliquid" role="tab" aria-controls="tracker-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="tracker-kraken-tab" data-toggle="tab" href="#tracker-kraken" role="tab" aria-controls="tracker-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="tracker-kraken-tab" data-toggle="tab" href="#tracker-kraken" role="tab" aria-controls="tracker-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -3582,6 +3889,9 @@ foreach (var book in books.Where(b => b.Status == OrderBookStatus.Synced))
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="tracker-whitebit-tab" data-toggle="tab" href="#tracker-whitebit" role="tab" aria-controls="tracker-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="tracker-whitebit-tab" data-toggle="tab" href="#tracker-whitebit" role="tab" aria-controls="tracker-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="tracker-xt-tab" data-toggle="tab" href="#tracker-xt" role="tab" aria-controls="tracker-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="tracker-cryptoclients" role="tabpanel" aria-labelledby="tracker-cryptoclients-tab">
|
<div class="tab-pane fade show active" id="tracker-cryptoclients" role="tabpanel" aria-labelledby="tracker-cryptoclients-tab">
|
||||||
@@ -3821,6 +4131,26 @@ if (!startResult.Success)
|
|||||||
// Tracker has successfully started
|
// Tracker has successfully started
|
||||||
// Note that it might not be fully synced yet, check tracker.Status for this.
|
// Note that it might not be fully synced yet, check tracker.Status for this.
|
||||||
|
|
||||||
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
|
await tracker.StopAsync();
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="tracker-hyperliquid" role="tabpanel" aria-labelledby="tracker-hyperliquid-tab">
|
||||||
|
<pre><code>// Either create a new factory or inject the IHyperLiquidTrackerFactory interface
|
||||||
|
var factory = new HyperLiquidTrackerFactory();
|
||||||
|
|
||||||
|
var symbol = new SharedSymbol(TradingMode.Spot, "HYPE", "USDC");
|
||||||
|
|
||||||
|
// Create a tracker for HYPE/USDC keeping track of trades in the last 5 minutes
|
||||||
|
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
|
||||||
|
var startResult = await tracker.StartAsync();
|
||||||
|
if (!startResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, error info available in startResult.Error
|
||||||
|
}
|
||||||
|
// Tracker has successfully started
|
||||||
|
// Note that it might not be fully synced yet, check tracker.Status for this.
|
||||||
|
|
||||||
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
await tracker.StopAsync();
|
await tracker.StopAsync();
|
||||||
</code></pre>
|
</code></pre>
|
||||||
@@ -3921,6 +4251,26 @@ if (!startResult.Success)
|
|||||||
// Tracker has successfully started
|
// Tracker has successfully started
|
||||||
// Note that it might not be fully synced yet, check tracker.Status for this.
|
// Note that it might not be fully synced yet, check tracker.Status for this.
|
||||||
|
|
||||||
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
|
await tracker.StopAsync();
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="tracker-xt" role="tabpanel" aria-labelledby="tracker-xt-tab">
|
||||||
|
<pre><code>// Either create a new factory or inject the IXTTrackerFactory interface
|
||||||
|
var factory = new XTTrackerFactory();
|
||||||
|
|
||||||
|
var symbol = new SharedSymbol(TradingMode.Spot, "ETH", "USDT");
|
||||||
|
|
||||||
|
// Create a tracker for ETH/USDT keeping track of trades in the last 5 minutes
|
||||||
|
var tracker = factory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5));
|
||||||
|
var startResult = await tracker.StartAsync();
|
||||||
|
if (!startResult.Success)
|
||||||
|
{
|
||||||
|
// Handle error, error info available in startResult.Error
|
||||||
|
}
|
||||||
|
// Tracker has successfully started
|
||||||
|
// Note that it might not be fully synced yet, check tracker.Status for this.
|
||||||
|
|
||||||
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
// Once no longer needed you can stop the live sync functionality by calling StopAsync()
|
||||||
await tracker.StopAsync();
|
await tracker.StopAsync();
|
||||||
</code></pre>
|
</code></pre>
|
||||||
@@ -4259,6 +4609,9 @@ var binanceClient = new BinanceRestClient(new HttpClient(), logFactory, options
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="limit-htx-tab" data-toggle="tab" href="#limit-htx" role="tab" aria-controls="limit-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="limit-htx-tab" data-toggle="tab" href="#limit-htx" role="tab" aria-controls="limit-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="limit-hyperliquid-tab" data-toggle="tab" href="#limit-hyperliquid" role="tab" aria-controls="limit-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="limit-kraken-tab" data-toggle="tab" href="#limit-kraken" role="tab" aria-controls="limit-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="limit-kraken-tab" data-toggle="tab" href="#limit-kraken" role="tab" aria-controls="limit-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -4274,6 +4627,9 @@ var binanceClient = new BinanceRestClient(new HttpClient(), logFactory, options
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="limit-whitebit-tab" data-toggle="tab" href="#limit-whitebit" role="tab" aria-controls="limit-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="limit-whitebit-tab" data-toggle="tab" href="#limit-whitebit" role="tab" aria-controls="limit-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="limit-xt-tab" data-toggle="tab" href="#limit-xt" role="tab" aria-controls="limit-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="limit-cc" role="tabpanel" aria-labelledby="limit-cc-tab">
|
<div class="tab-pane fade show active" id="limit-cc" role="tabpanel" aria-labelledby="limit-cc-tab">
|
||||||
@@ -4398,6 +4754,20 @@ var binanceClient = new BinanceRestClient(new HttpClient(), logFactory, options
|
|||||||
<p>To be notified of when a rate limit is hit the static <code>HTXExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
<p>To be notified of when a rate limit is hit the static <code>HTXExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
||||||
<pre><code>HTXExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
<pre><code>HTXExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="limit-hyperliquid" role="tabpanel" aria-labelledby="limit-hyperliquid-tab">
|
||||||
|
<pre><code>services.AddHyperLiquid(x =>
|
||||||
|
x.RatelimiterEnabled = true;
|
||||||
|
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
|
||||||
|
}, x =>
|
||||||
|
{
|
||||||
|
x.RatelimiterEnabled = true;
|
||||||
|
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
|
||||||
|
});</code></pre>
|
||||||
|
<p>To be notified of when a rate limit is hit the static <code>HyperLiquidExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
||||||
|
<pre><code>HyperLiquidExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
||||||
|
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="limit-kraken" role="tabpanel" aria-labelledby="limit-kraken-tab">
|
<div class="tab-pane fade" id="limit-kraken" role="tabpanel" aria-labelledby="limit-kraken-tab">
|
||||||
@@ -4474,6 +4844,20 @@ var binanceClient = new BinanceRestClient(new HttpClient(), logFactory, options
|
|||||||
<p>To be notified of when a rate limit is hit the static <code>WhiteBitExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
<p>To be notified of when a rate limit is hit the static <code>WhiteBitExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
||||||
<pre><code>WhiteBitExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
<pre><code>WhiteBitExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="limit-xt" role="tabpanel" aria-labelledby="limit-xt-tab">
|
||||||
|
<pre><code>services.AddXT(x =>
|
||||||
|
x.RatelimiterEnabled = true;
|
||||||
|
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
|
||||||
|
}, x =>
|
||||||
|
{
|
||||||
|
x.RatelimiterEnabled = true;
|
||||||
|
x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
|
||||||
|
});</code></pre>
|
||||||
|
<p>To be notified of when a rate limit is hit the static <code>XTExchange.RateLimiter</code> exposes an event which triggers when a rate limit is reached</p>
|
||||||
|
<pre><code>XTExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
|
||||||
|
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4561,6 +4945,9 @@ var responseSource = result.DataSource;</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-symbols-htx-tab" data-toggle="tab" href="#example-symbols-htx" role="tab" aria-controls="example-symbols-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-symbols-htx-tab" data-toggle="tab" href="#example-symbols-htx" role="tab" aria-controls="example-symbols-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-symbols-hyperliquid-tab" data-toggle="tab" href="#example-symbols-hyperliquid" role="tab" aria-controls="example-symbols-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-symbols-kraken-tab" data-toggle="tab" href="#example-symbols-kraken" role="tab" aria-controls="example-symbols-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-symbols-kraken-tab" data-toggle="tab" href="#example-symbols-kraken" role="tab" aria-controls="example-symbols-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -4576,6 +4963,9 @@ var responseSource = result.DataSource;</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-symbols-whitebit-tab" data-toggle="tab" href="#example-symbols-whitebit" role="tab" aria-controls="example-symbols-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-symbols-whitebit-tab" data-toggle="tab" href="#example-symbols-whitebit" role="tab" aria-controls="example-symbols-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-symbols-xt-tab" data-toggle="tab" href="#example-symbols-xt" role="tab" aria-controls="example-symbols-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="example-symbols-general" role="tabpanel" aria-labelledby="example-symbols-general-tab">
|
<div class="tab-pane fade show active" id="example-symbols-general" role="tabpanel" aria-labelledby="example-symbols-general-tab">
|
||||||
@@ -4621,6 +5011,9 @@ await exchangeRestClient.GetSpotSymbolsAsync(new GetSymbolsRequest(), ["Binance"
|
|||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-symbols-htx" role="tabpanel" aria-labelledby="example-symbols-htx-tab">
|
<div class="tab-pane fade" id="example-symbols-htx" role="tabpanel" aria-labelledby="example-symbols-htx-tab">
|
||||||
<pre><code>await htxClient.SpotApi.ExchangeData.GetSymbolsAsync();</code></pre>
|
<pre><code>await htxClient.SpotApi.ExchangeData.GetSymbolsAsync();</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-symbols-hyperliquid" role="tabpanel" aria-labelledby="example-symbols-hyperliquid-tab">
|
||||||
|
<pre><code>await hyperLiquidClient.SpotApi.ExchangeData.GetExchangeInfoAsync();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-symbols-kraken" role="tabpanel" aria-labelledby="example-symbols-kraken-tab">
|
<div class="tab-pane fade" id="example-symbols-kraken" role="tabpanel" aria-labelledby="example-symbols-kraken-tab">
|
||||||
<pre><code>await krakenClient.SpotApi.ExchangeData.GetSymbolsAsync();</code></pre>
|
<pre><code>await krakenClient.SpotApi.ExchangeData.GetSymbolsAsync();</code></pre>
|
||||||
@@ -4637,6 +5030,9 @@ await exchangeRestClient.GetSpotSymbolsAsync(new GetSymbolsRequest(), ["Binance"
|
|||||||
<div class="tab-pane fade" id="example-symbols-whitebit" role="tabpanel" aria-labelledby="example-symbols-whitebit-tab">
|
<div class="tab-pane fade" id="example-symbols-whitebit" role="tabpanel" aria-labelledby="example-symbols-whitebit-tab">
|
||||||
<pre><code>await whitebitClient.V4Api.ExchangeData.GetSymbolsAsync();</code></pre>
|
<pre><code>await whitebitClient.V4Api.ExchangeData.GetSymbolsAsync();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-symbols-xt" role="tabpanel" aria-labelledby="example-symbols-xt-tab">
|
||||||
|
<pre><code>await xtClient.SpotApi.ExchangeData.GetSymbolsAsync();</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4691,6 +5087,9 @@ await exchangeRestClient.GetSpotSymbolsAsync(new GetSymbolsRequest(), ["Binance"
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-ticker-htx-tab" data-toggle="tab" href="#example-ticker-htx" role="tab" aria-controls="example-ticker-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-ticker-htx-tab" data-toggle="tab" href="#example-ticker-htx" role="tab" aria-controls="example-ticker-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-ticker-hyperliquid-tab" data-toggle="tab" href="#example-ticker-hyperliquid" role="tab" aria-controls="example-ticker-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-ticker-kraken-tab" data-toggle="tab" href="#example-ticker-kraken" role="tab" aria-controls="example-ticker-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-ticker-kraken-tab" data-toggle="tab" href="#example-ticker-kraken" role="tab" aria-controls="example-ticker-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -4706,6 +5105,9 @@ await exchangeRestClient.GetSpotSymbolsAsync(new GetSymbolsRequest(), ["Binance"
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-ticker-whitebit-tab" data-toggle="tab" href="#example-ticker-whitebit" role="tab" aria-controls="example-ticker-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-ticker-whitebit-tab" data-toggle="tab" href="#example-ticker-whitebit" role="tab" aria-controls="example-ticker-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-ticker-xt-tab" data-toggle="tab" href="#example-ticker-xt" role="tab" aria-controls="example-ticker-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="example-ticker-general" role="tabpanel" aria-labelledby="example-ticker-general-tab">
|
<div class="tab-pane fade show active" id="example-ticker-general" role="tabpanel" aria-labelledby="example-ticker-general-tab">
|
||||||
@@ -4753,6 +5155,11 @@ await coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolAsync("BTC-USDT");</
|
|||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-ticker-htx" role="tabpanel" aria-labelledby="example-ticker-htx-tab">
|
<div class="tab-pane fade" id="example-ticker-htx" role="tabpanel" aria-labelledby="example-ticker-htx-tab">
|
||||||
<pre><code>await htxClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");</code></pre>
|
<pre><code>await htxClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-ticker-hyperliquid" role="tabpanel" aria-labelledby="example-ticker-hyperliquid-tab">
|
||||||
|
<pre><code>// HyperLiquid API doesn't offer a symbol filter, so we have to filter client side
|
||||||
|
var tickersResult = await hyperLiquidClient.SpotApi.ExchangeData.GetExchangeInfoAndTickersAsync();
|
||||||
|
var ticker = tickersResult.Data.Tickers.Single(x => x.Symbol == "HYPE/USDC");</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-ticker-kraken" role="tabpanel" aria-labelledby="example-ticker-kraken-tab">
|
<div class="tab-pane fade" id="example-ticker-kraken" role="tabpanel" aria-labelledby="example-ticker-kraken-tab">
|
||||||
<pre><code>await krakenClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");</code></pre>
|
<pre><code>await krakenClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");</code></pre>
|
||||||
@@ -4770,6 +5177,9 @@ await coinbaseClient.AdvancedTradeApi.ExchangeData.GetSymbolAsync("BTC-USDT");</
|
|||||||
<pre><code>// WhiteBit API doesn't offer a symbol filter, so we have to filter client side
|
<pre><code>// WhiteBit API doesn't offer a symbol filter, so we have to filter client side
|
||||||
var tickersResult = await whitebitClient.V4Api.ExchangeData.GetTickersAsync();
|
var tickersResult = await whitebitClient.V4Api.ExchangeData.GetTickersAsync();
|
||||||
var ticker = tickersResult.Data.Single(x => x.Symbol == "BTC_USDT");</code></pre>
|
var ticker = tickersResult.Data.Single(x => x.Symbol == "BTC_USDT");</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-ticker-xt" role="tabpanel" aria-labelledby="example-ticker-xt-tab">
|
||||||
|
<pre><code>await xtClient.SpotApi.ExchangeData.GetTickersAsync("btc-usdt");</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4825,6 +5235,9 @@ var ticker = tickersResult.Data.Single(x => x.Symbol == "BTC_USDT");</code></pre
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-balances-htx-tab" data-toggle="tab" href="#example-balances-htx" role="tab" aria-controls="example-balances-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-balances-htx-tab" data-toggle="tab" href="#example-balances-htx" role="tab" aria-controls="example-balances-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-balances-hyperliquid-tab" data-toggle="tab" href="#example-balances-hyperliquid" role="tab" aria-controls="example-balances-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-balances-kraken-tab" data-toggle="tab" href="#example-balances-kraken" role="tab" aria-controls="example-balances-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-balances-kraken-tab" data-toggle="tab" href="#example-balances-kraken" role="tab" aria-controls="example-balances-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -4840,6 +5253,9 @@ var ticker = tickersResult.Data.Single(x => x.Symbol == "BTC_USDT");</code></pre
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-balances-whitebit-tab" data-toggle="tab" href="#example-balances-whitebit" role="tab" aria-controls="example-balances-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-balances-whitebit-tab" data-toggle="tab" href="#example-balances-whitebit" role="tab" aria-controls="example-balances-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-balances-xt-tab" data-toggle="tab" href="#example-balances-xt" role="tab" aria-controls="example-balances-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="example-balances-general" role="tabpanel" aria-labelledby="example-balances-general-tab">
|
<div class="tab-pane fade show active" id="example-balances-general" role="tabpanel" aria-labelledby="example-balances-general-tab">
|
||||||
@@ -4889,6 +5305,9 @@ var accounts = await htxClient.SpotApi.Account.GetAccountsAsync();
|
|||||||
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
|
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
|
||||||
|
|
||||||
var result = await htxClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
var result = await htxClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-balances-hyperliquid" role="tabpanel" aria-labelledby="example-balances-hyperliquid-tab">
|
||||||
|
<pre><code>await hyperLiquidClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-balances-kraken" role="tabpanel" aria-labelledby="example-balances-kraken-tab">
|
<div class="tab-pane fade" id="example-balances-kraken" role="tabpanel" aria-labelledby="example-balances-kraken-tab">
|
||||||
<pre><code>await krakenClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
<pre><code>await krakenClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
||||||
@@ -4905,6 +5324,9 @@ var result = await htxClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
|||||||
<div class="tab-pane fade" id="example-balances-whitebit" role="tabpanel" aria-labelledby="example-balances-whitebit-tab">
|
<div class="tab-pane fade" id="example-balances-whitebit" role="tabpanel" aria-labelledby="example-balances-whitebit-tab">
|
||||||
<pre><code>await whitebitClient.V4Api.Account.GetSpotBalancesAsync();</code></pre>
|
<pre><code>await whitebitClient.V4Api.Account.GetSpotBalancesAsync();</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-balances-xt" role="tabpanel" aria-labelledby="example-balances-xt-tab">
|
||||||
|
<pre><code>await xtClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4959,6 +5381,9 @@ var result = await htxClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-place-htx-tab" data-toggle="tab" href="#example-place-htx" role="tab" aria-controls="example-place-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-place-htx-tab" data-toggle="tab" href="#example-place-htx" role="tab" aria-controls="example-place-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-place-hyperliquid-tab" data-toggle="tab" href="#example-place-hyperliquid" role="tab" aria-controls="example-place-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-place-kraken-tab" data-toggle="tab" href="#example-place-kraken" role="tab" aria-controls="example-place-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-place-kraken-tab" data-toggle="tab" href="#example-place-kraken" role="tab" aria-controls="example-place-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -4972,7 +5397,10 @@ var result = await htxClient.SpotApi.Account.GetBalancesAsync();</code></pre>
|
|||||||
<a class="nav-link" id="example-place-okx-tab" data-toggle="tab" href="#example-place-okx" role="tab" aria-controls="example-place-okx" aria-selected="false">OKX</a>
|
<a class="nav-link" id="example-place-okx-tab" data-toggle="tab" href="#example-place-okx" role="tab" aria-controls="example-place-okx" aria-selected="false">OKX</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-place-okx-tab" data-toggle="tab" href="#example-place-okx" role="tab" aria-controls="example-place-okx" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-place-whitebit-tab" data-toggle="tab" href="#example-place-whitebit" role="tab" aria-controls="example-place-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-place-xt-tab" data-toggle="tab" href="#example-place-xt" role="tab" aria-controls="example-place-xt" aria-selected="false">XT</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
@@ -5021,6 +5449,10 @@ var accounts = await htxClient.SpotApi.Account.GetAccountsAsync();
|
|||||||
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
|
var account = accounts.Data.Single(a => a.Type == AccountType.Spot);
|
||||||
|
|
||||||
var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000);</code></pre>
|
var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSDT", OrderSide.Buy, OrderType.Limit, 0.1m, price: 50000);</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-place-hyperliquid" role="tabpanel" aria-labelledby="example-place-hyperliquid-tab">
|
||||||
|
<pre><code>// BTC not support on HyperLiquid Spot trading, example uses HYPE/USDC Pair
|
||||||
|
await hyperLiquidClient.SpotApi.Trading.PlaceOrderAsync("HYPE/USDC",OrderSide.Buy, OrderType.Limit, 1m, 20);</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-place-kraken" role="tabpanel" aria-labelledby="example-place-kraken-tab">
|
<div class="tab-pane fade" id="example-place-kraken" role="tabpanel" aria-labelledby="example-place-kraken-tab">
|
||||||
<pre><code>await krakenClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT",OrderSide.Buy, OrderType.Limit, 0.1m, 50000);</code></pre>
|
<pre><code>await krakenClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT",OrderSide.Buy, OrderType.Limit, 0.1m, 50000);</code></pre>
|
||||||
@@ -5037,6 +5469,9 @@ var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSD
|
|||||||
<div class="tab-pane fade" id="example-place-whitebit" role="tabpanel" aria-labelledby="example-place-whitebit-tab">
|
<div class="tab-pane fade" id="example-place-whitebit" role="tabpanel" aria-labelledby="example-place-whitebit-tab">
|
||||||
<pre><code>await whitebitClient.V4Api.Trading.PlaceSpotOrderAsync("BTC_USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);</code></pre>
|
<pre><code>await whitebitClient.V4Api.Trading.PlaceSpotOrderAsync("BTC_USDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-place-xt" role="tabpanel" aria-labelledby="example-place-xt-tab">
|
||||||
|
<pre><code>await xtClient.SpotApi.Trading.PlaceOrderAsync("eth_usdt", OrderSide.Buy, OrderType.Limit, TimeInForce.GoodTillCanceled, BusinessType.Spot, 0.1m, price: 50000);</code></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -5091,6 +5526,9 @@ var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSD
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-ticker-htx-tab" data-toggle="tab" href="#example-stream-ticker-htx" role="tab" aria-controls="example-stream-ticker-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-stream-ticker-htx-tab" data-toggle="tab" href="#example-stream-ticker-htx" role="tab" aria-controls="example-stream-ticker-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-stream-ticker-hyperliquid-tab" data-toggle="tab" href="#example-stream-ticker-hyperliquid" role="tab" aria-controls="example-stream-ticker-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-ticker-kraken-tab" data-toggle="tab" href="#example-stream-ticker-kraken" role="tab" aria-controls="example-stream-ticker-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-stream-ticker-kraken-tab" data-toggle="tab" href="#example-stream-ticker-kraken" role="tab" aria-controls="example-stream-ticker-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -5106,6 +5544,9 @@ var result = await htxClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCUSD
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-ticker-whitebit-tab" data-toggle="tab" href="#example-stream-ticker-whitebit" role="tab" aria-controls="example-stream-ticker-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-stream-ticker-whitebit-tab" data-toggle="tab" href="#example-stream-ticker-whitebit" role="tab" aria-controls="example-stream-ticker-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-stream-ticker-xt-tab" data-toggle="tab" href="#example-stream-ticker-xt" role="tab" aria-controls="example-stream-ticker-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="example-stream-ticker-cc" role="tabpanel" aria-labelledby="example-stream-ticker-cc-tab">
|
<div class="tab-pane fade show active" id="example-stream-ticker-cc" role="tabpanel" aria-labelledby="example-stream-ticker-cc-tab">
|
||||||
@@ -5174,6 +5615,11 @@ await exchangeSocketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequ
|
|||||||
<div class="tab-pane fade" id="example-stream-ticker-gateio" role="tabpanel" aria-labelledby="example-stream-ticker-gateio-tab">
|
<div class="tab-pane fade" id="example-stream-ticker-gateio" role="tabpanel" aria-labelledby="example-stream-ticker-gateio-tab">
|
||||||
<pre><code>await gateioSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
|
<pre><code>await gateioSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
|
||||||
// Handle update
|
// Handle update
|
||||||
|
});</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-stream-ticker-hyperliquid" role="tabpanel" aria-labelledby="example-stream-ticker-hyperliquid-tab">
|
||||||
|
<pre><code>await hyperLiquidSocketClient.SpotApi.SubscribeToSymbolUpdatesAsync("HYPE/USDC", data => {
|
||||||
|
// Handle update
|
||||||
});</code></pre>
|
});</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-stream-ticker-htx" role="tabpanel" aria-labelledby="example-stream-ticker-htx-tab">
|
<div class="tab-pane fade" id="example-stream-ticker-htx" role="tabpanel" aria-labelledby="example-stream-ticker-htx-tab">
|
||||||
@@ -5206,6 +5652,12 @@ await exchangeSocketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequ
|
|||||||
<pre><code>await whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
|
<pre><code>await whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_USDT", data => {
|
||||||
// Handle update
|
// Handle update
|
||||||
});
|
});
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-stream-ticker-xt" role="tabpanel" aria-labelledby="example-stream-ticker-xt-tab">
|
||||||
|
<pre><code>await xtSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("eth_usdt", data => {
|
||||||
|
// Handle update
|
||||||
|
});
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -5262,6 +5714,9 @@ await exchangeSocketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequ
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-order-htx-tab" data-toggle="tab" href="#example-stream-order-htx" role="tab" aria-controls="example-stream-order-htx" aria-selected="false">HTX</a>
|
<a class="nav-link" id="example-stream-order-htx-tab" data-toggle="tab" href="#example-stream-order-htx" role="tab" aria-controls="example-stream-order-htx" aria-selected="false">HTX</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-stream-order-hyperliquid-tab" data-toggle="tab" href="#example-stream-order-hyperliquid" role="tab" aria-controls="example-stream-order-hyperliquid" aria-selected="false">HyperLiquid</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-order-kraken-tab" data-toggle="tab" href="#example-stream-order-kraken" role="tab" aria-controls="example-stream-order-kraken" aria-selected="false">Kraken</a>
|
<a class="nav-link" id="example-stream-order-kraken-tab" data-toggle="tab" href="#example-stream-order-kraken" role="tab" aria-controls="example-stream-order-kraken" aria-selected="false">Kraken</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -5277,6 +5732,9 @@ await exchangeSocketClient.SubscribeToTickerUpdatesAsync(new SubscribeTickerRequ
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link" id="example-stream-order-whitebit-tab" data-toggle="tab" href="#example-stream-order-whitebit" role="tab" aria-controls="example-stream-order-whitebit" aria-selected="false">WhiteBit</a>
|
<a class="nav-link" id="example-stream-order-whitebit-tab" data-toggle="tab" href="#example-stream-order-whitebit" role="tab" aria-controls="example-stream-order-whitebit" aria-selected="false">WhiteBit</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<a class="nav-link" id="example-stream-order-xt-tab" data-toggle="tab" href="#example-stream-order-xt" role="tab" aria-controls="example-stream-order-xt" aria-selected="false">XT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content my-3" id="myTabContent">
|
<div class="tab-content my-3" id="myTabContent">
|
||||||
<div class="tab-pane fade show active" id="example-stream-order-cc" role="tabpanel" aria-labelledby="example-stream-order-cc-tab">
|
<div class="tab-pane fade show active" id="example-stream-order-cc" role="tabpanel" aria-labelledby="example-stream-order-cc-tab">
|
||||||
@@ -5386,6 +5844,11 @@ _ = Task.Run(async () => {
|
|||||||
<div class="tab-pane fade" id="example-stream-order-htx" role="tabpanel" aria-labelledby="example-stream-order-htx-tab">
|
<div class="tab-pane fade" id="example-stream-order-htx" role="tabpanel" aria-labelledby="example-stream-order-htx-tab">
|
||||||
<pre><code>await htxSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(onOrderMatched: data => {
|
<pre><code>await htxSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(onOrderMatched: data => {
|
||||||
// Handle update
|
// Handle update
|
||||||
|
});</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-stream-order-hyperliquid" role="tabpanel" aria-labelledby="example-stream-order-hyperliquid-tab">
|
||||||
|
<pre><code>await hyperLiquidSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(null, data => {
|
||||||
|
// Handle update
|
||||||
});</code></pre>
|
});</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="example-stream-order-kraken" role="tabpanel" aria-labelledby="example-stream-order-kraken-tab">
|
<div class="tab-pane fade" id="example-stream-order-kraken" role="tabpanel" aria-labelledby="example-stream-order-kraken-tab">
|
||||||
@@ -5432,6 +5895,26 @@ _ = Task.Run(async () => {
|
|||||||
await whitebitSocketClient.V4Api.SubscribeToOpenOrderUpdatesAsync(["ETH_USDT", "BTC_USDT"], data => {
|
await whitebitSocketClient.V4Api.SubscribeToOpenOrderUpdatesAsync(["ETH_USDT", "BTC_USDT"], data => {
|
||||||
// Handle update
|
// Handle update
|
||||||
});
|
});
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="example-stream-order-xt" role="tabpanel" aria-labelledby="example-stream-order-xt-tab">
|
||||||
|
<pre><code>// Retrieve the token
|
||||||
|
var listenKey = await xtRestClient.SpotApi.Account.GetWebsocketTokenAsync();
|
||||||
|
|
||||||
|
// Subscribe using the key
|
||||||
|
await xtSocketClient.SpotApi.SubscribeToBalanceUpdatesAsync(listenKey.Data, data => {
|
||||||
|
// Handle update
|
||||||
|
});
|
||||||
|
|
||||||
|
// The listen key will stay valid for 48 hours, after this no updates will be send anymore
|
||||||
|
// To extend the life time of the token it is recommended to call the GetWebsocketTokenAsync method at a set interval which will extend the lifetime
|
||||||
|
_ = Task.Run(async () => {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
await Task.Delay(Timespan.FromHours(4));
|
||||||
|
await xtRestClient.SpotApi.Account.GetWebsocketTokenAsync();
|
||||||
|
}
|
||||||
|
});
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user