mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-12 17:03:10 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b513e51b9 | |||
| d43b38a23a | |||
| 0987c0f9d1 | |||
| e2dde77023 | |||
| 104ac7caad | |||
| 8788dd3deb | |||
| f64cc5e9cf | |||
| 75d1bbc6e8 | |||
| b621aa7e65 | |||
| 9783108695 | |||
| 6ba32fe280 | |||
| f75cc75bbc | |||
| 2109b65a8e | |||
| a472751638 | |||
| f08ed16f2a | |||
| 212d457a6a |
@@ -115,6 +115,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
TimeSpan.FromSeconds(1),
|
TimeSpan.FromSeconds(1),
|
||||||
null,
|
null,
|
||||||
"{}",
|
"{}",
|
||||||
|
1,
|
||||||
"https://test.com/api",
|
"https://test.com/api",
|
||||||
null,
|
null,
|
||||||
HttpMethod.Get,
|
HttpMethod.Get,
|
||||||
@@ -143,6 +144,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
TimeSpan.FromSeconds(1),
|
TimeSpan.FromSeconds(1),
|
||||||
null,
|
null,
|
||||||
"{}",
|
"{}",
|
||||||
|
1,
|
||||||
"https://test.com/api",
|
"https://test.com/api",
|
||||||
null,
|
null,
|
||||||
HttpMethod.Get,
|
HttpMethod.Get,
|
||||||
|
|||||||
@@ -60,11 +60,6 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
headers = new Dictionary<string, string>();
|
headers = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Sign(string toSign)
|
|
||||||
{
|
|
||||||
return toSign;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetKey() => _credentials.Key.GetString();
|
public string GetKey() => _credentials.Key.GetString();
|
||||||
public string GetSecret() => _credentials.Secret.GetString();
|
public string GetSecret() => _credentials.Secret.GetString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,17 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA256 sign the data and return the bytes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignSHA256Bytes(byte[] data)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA256.Create();
|
||||||
|
return encryptor.ComputeHash(data);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SHA256 sign the data and return the hash
|
/// SHA256 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -87,6 +98,19 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA256 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static string SignSHA256(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA256.Create();
|
||||||
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SHA384 sign the data and return the hash
|
/// SHA384 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -100,6 +124,41 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA384 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static string SignSHA384(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA384.Create();
|
||||||
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA384 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignSHA384Bytes(string data)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA384.Create();
|
||||||
|
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA384 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignSHA384Bytes(byte[] data)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA384.Create();
|
||||||
|
return encryptor.ComputeHash(data);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SHA512 sign the data and return the hash
|
/// SHA512 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -113,6 +172,41 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA512 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static string SignSHA512(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA512.Create();
|
||||||
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA512 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignSHA512Bytes(string data)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA512.Create();
|
||||||
|
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA512 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignSHA512Bytes(byte[] data)
|
||||||
|
{
|
||||||
|
using var encryptor = SHA512.Create();
|
||||||
|
return encryptor.ComputeHash(data);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MD5 sign the data and return the hash
|
/// MD5 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -127,28 +221,70 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HMACSHA256 sign the data and return the hash
|
/// MD5 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static string SignMD5(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var encryptor = MD5.Create();
|
||||||
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MD5 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected static byte[] SignMD5Bytes(string data)
|
||||||
|
{
|
||||||
|
using var encryptor = MD5.Create();
|
||||||
|
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HMACSHA512 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Data to sign</param>
|
/// <param name="data">Data to sign</param>
|
||||||
/// <param name="outputType">String type</param>
|
/// <param name="outputType">String type</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected string SignHMACSHA256(string data, SignOutputType? outputType = null)
|
protected string SignHMACSHA256(string data, SignOutputType? outputType = null)
|
||||||
|
=> SignHMACSHA256(Encoding.UTF8.GetBytes(data), outputType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HMACSHA256 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string SignHMACSHA256(byte[] data, SignOutputType? outputType = null)
|
||||||
{
|
{
|
||||||
using var encryptor = new HMACSHA256(_sBytes);
|
using var encryptor = new HMACSHA256(_sBytes);
|
||||||
var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HMACSHA512 sign the data and return the hash
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Data to sign</param>
|
||||||
|
/// <param name="outputType">String type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string SignHMACSHA384(string data, SignOutputType? outputType = null)
|
||||||
|
=> SignHMACSHA384(Encoding.UTF8.GetBytes(data), outputType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HMACSHA384 sign the data and return the hash
|
/// HMACSHA384 sign the data and return the hash
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Data to sign</param>
|
/// <param name="data">Data to sign</param>
|
||||||
/// <param name="outputType">String type</param>
|
/// <param name="outputType">String type</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected string SignHMACSHA384(string data, SignOutputType? outputType = null)
|
protected string SignHMACSHA384(byte[] data, SignOutputType? outputType = null)
|
||||||
{
|
{
|
||||||
using var encryptor = new HMACSHA384(_sBytes);
|
using var encryptor = new HMACSHA384(_sBytes);
|
||||||
var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
|
var resultBytes = encryptor.ComputeHash(data);
|
||||||
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +318,46 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected string SignRSASHA256(byte[] data, SignOutputType? outputType = null)
|
protected string SignRSASHA256(byte[] data, SignOutputType? outputType = null)
|
||||||
{
|
{
|
||||||
using var rsa = RSA.Create();
|
using var rsa = CreateRSA();
|
||||||
|
using var sha256 = SHA256.Create();
|
||||||
|
var hash = sha256.ComputeHash(data);
|
||||||
|
var resultBytes = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
|
return outputType == SignOutputType.Base64? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA384 sign the data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="outputType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string SignRSASHA384(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var rsa = CreateRSA();
|
||||||
|
using var sha384 = SHA384.Create();
|
||||||
|
var hash = sha384.ComputeHash(data);
|
||||||
|
var resultBytes = rsa.SignHash(hash, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SHA512 sign the data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="outputType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string SignRSASHA512(byte[] data, SignOutputType? outputType = null)
|
||||||
|
{
|
||||||
|
using var rsa = CreateRSA();
|
||||||
|
using var sha512 = SHA512.Create();
|
||||||
|
var hash = sha512.ComputeHash(data);
|
||||||
|
var resultBytes = rsa.SignHash(hash, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||||
|
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RSA CreateRSA()
|
||||||
|
{
|
||||||
|
var rsa = RSA.Create();
|
||||||
if (_credentials.CredentialType == ApiCredentialsType.RsaPem)
|
if (_credentials.CredentialType == ApiCredentialsType.RsaPem)
|
||||||
{
|
{
|
||||||
#if NETSTANDARD2_1_OR_GREATER
|
#if NETSTANDARD2_1_OR_GREATER
|
||||||
@@ -209,30 +384,7 @@ namespace CryptoExchange.Net.Authentication
|
|||||||
throw new Exception("Invalid credentials type");
|
throw new Exception("Invalid credentials type");
|
||||||
}
|
}
|
||||||
|
|
||||||
using var sha256 = SHA256.Create();
|
return rsa;
|
||||||
var hash = sha256.ComputeHash(data);
|
|
||||||
var resultBytes = rsa.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
|
||||||
return outputType == SignOutputType.Base64? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sign a string
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSign"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public virtual string Sign(string toSign)
|
|
||||||
{
|
|
||||||
return toSign;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sign a byte array
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toSign"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public virtual byte[] Sign(byte[] toSign)
|
|
||||||
{
|
|
||||||
return toSign;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Net.Http;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CryptoExchange.Net.Authentication;
|
using CryptoExchange.Net.Authentication;
|
||||||
|
using CryptoExchange.Net.Converters;
|
||||||
using CryptoExchange.Net.Interfaces;
|
using CryptoExchange.Net.Interfaces;
|
||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
using CryptoExchange.Net.Objects.Options;
|
using CryptoExchange.Net.Objects.Options;
|
||||||
@@ -78,13 +79,9 @@ namespace CryptoExchange.Net
|
|||||||
public bool OutputOriginalData { get; }
|
public bool OutputOriginalData { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default serializer
|
/// The default serializer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly JsonSerializer _defaultSerializer = JsonSerializer.Create(new JsonSerializerSettings
|
protected virtual JsonSerializer DefaultSerializer { get; set; } = JsonSerializer.Create(SerializerOptions.Default);
|
||||||
{
|
|
||||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
|
||||||
Culture = CultureInfo.InvariantCulture
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Api options
|
/// Api options
|
||||||
@@ -204,7 +201,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected CallResult<T> Deserialize<T>(JToken obj, JsonSerializer? serializer = null, int? requestId = null)
|
protected CallResult<T> Deserialize<T>(JToken obj, JsonSerializer? serializer = null, int? requestId = null)
|
||||||
{
|
{
|
||||||
serializer ??= _defaultSerializer;
|
serializer ??= DefaultSerializer;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -242,7 +239,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected async Task<CallResult<T>> DeserializeAsync<T>(Stream stream, JsonSerializer? serializer = null, int? requestId = null, long? elapsedMilliseconds = null)
|
protected async Task<CallResult<T>> DeserializeAsync<T>(Stream stream, JsonSerializer? serializer = null, int? requestId = null, long? elapsedMilliseconds = null)
|
||||||
{
|
{
|
||||||
serializer ??= _defaultSerializer;
|
serializer ??= DefaultSerializer;
|
||||||
string? data = null;
|
string? data = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
public virtual void Initialize(ExchangeOptions options)
|
protected virtual void Initialize(ExchangeOptions options)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
throw new ArgumentNullException(nameof(options));
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="parameters">The parameters of the request</param>
|
/// <param name="parameters">The parameters of the request</param>
|
||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
|
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||||
/// <param name="requestWeight">Credits used for the request</param>
|
/// <param name="requestWeight">Credits used for the request</param>
|
||||||
@@ -97,6 +98,7 @@ namespace CryptoExchange.Net
|
|||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, object>? parameters = null,
|
Dictionary<string, object>? parameters = null,
|
||||||
bool signed = false,
|
bool signed = false,
|
||||||
|
RequestBodyFormat? requestBodyFormat = null,
|
||||||
HttpMethodParameterPosition? parameterPosition = null,
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
ArrayParametersSerialization? arraySerialization = null,
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
int requestWeight = 1,
|
int requestWeight = 1,
|
||||||
@@ -108,11 +110,16 @@ namespace CryptoExchange.Net
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
currentTry++;
|
currentTry++;
|
||||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||||
if (!request)
|
if (!request)
|
||||||
return new WebCallResult(request.Error!);
|
return new WebCallResult(request.Error!);
|
||||||
|
|
||||||
var result = await GetResponseAsync<object>(request.Data, deserializer, cancellationToken, true).ConfigureAwait(false);
|
var result = await GetResponseAsync<object>(request.Data, deserializer, cancellationToken, true).ConfigureAwait(false);
|
||||||
|
if (!result)
|
||||||
|
_logger.Log(LogLevel.Warning, $"[{result.RequestId}] Error received in {result.ResponseTime!.Value.TotalMilliseconds}ms: {result.Error}");
|
||||||
|
else
|
||||||
|
_logger.Log(LogLevel.Debug, $"[{result.RequestId}] Response received in {result.ResponseTime!.Value.TotalMilliseconds}ms{(OutputOriginalData ? (": " + result.OriginalData) : "")}");
|
||||||
|
|
||||||
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -129,6 +136,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="parameters">The parameters of the request</param>
|
/// <param name="parameters">The parameters of the request</param>
|
||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
|
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||||
/// <param name="requestWeight">Credits used for the request</param>
|
/// <param name="requestWeight">Credits used for the request</param>
|
||||||
@@ -143,6 +151,7 @@ namespace CryptoExchange.Net
|
|||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, object>? parameters = null,
|
Dictionary<string, object>? parameters = null,
|
||||||
bool signed = false,
|
bool signed = false,
|
||||||
|
RequestBodyFormat? requestBodyFormat = null,
|
||||||
HttpMethodParameterPosition? parameterPosition = null,
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
ArrayParametersSerialization? arraySerialization = null,
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
int requestWeight = 1,
|
int requestWeight = 1,
|
||||||
@@ -155,11 +164,16 @@ namespace CryptoExchange.Net
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
currentTry++;
|
currentTry++;
|
||||||
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
var request = await PrepareRequestAsync(uri, method, cancellationToken, parameters, signed, requestBodyFormat, parameterPosition, arraySerialization, requestWeight, deserializer, additionalHeaders, ignoreRatelimit).ConfigureAwait(false);
|
||||||
if (!request)
|
if (!request)
|
||||||
return new WebCallResult<T>(request.Error!);
|
return new WebCallResult<T>(request.Error!);
|
||||||
|
|
||||||
var result = await GetResponseAsync<T>(request.Data, deserializer, cancellationToken, false).ConfigureAwait(false);
|
var result = await GetResponseAsync<T>(request.Data, deserializer, cancellationToken, false).ConfigureAwait(false);
|
||||||
|
if (!result)
|
||||||
|
_logger.Log(LogLevel.Warning, $"[{result.RequestId}] Error received in {result.ResponseTime!.Value.TotalMilliseconds}ms: {result.Error}");
|
||||||
|
else
|
||||||
|
_logger.Log(LogLevel.Debug, $"[{result.RequestId}] Response received in {result.ResponseTime!.Value.TotalMilliseconds}ms{(OutputOriginalData ? (": " + result.OriginalData) : "")}");
|
||||||
|
|
||||||
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
if (await ShouldRetryRequestAsync(result, currentTry).ConfigureAwait(false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -175,6 +189,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="cancellationToken">Cancellation token</param>
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
/// <param name="parameters">The parameters of the request</param>
|
/// <param name="parameters">The parameters of the request</param>
|
||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
|
/// <param name="requestBodyFormat">The format of the body content</param>
|
||||||
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
/// <param name="parameterPosition">Where the parameters should be placed, overwrites the value set in the client</param>
|
||||||
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
/// <param name="arraySerialization">How array parameters should be serialized, overwrites the value set in the client</param>
|
||||||
/// <param name="requestWeight">Credits used for the request</param>
|
/// <param name="requestWeight">Credits used for the request</param>
|
||||||
@@ -188,6 +203,7 @@ namespace CryptoExchange.Net
|
|||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
Dictionary<string, object>? parameters = null,
|
Dictionary<string, object>? parameters = null,
|
||||||
bool signed = false,
|
bool signed = false,
|
||||||
|
RequestBodyFormat? requestBodyFormat = null,
|
||||||
HttpMethodParameterPosition? parameterPosition = null,
|
HttpMethodParameterPosition? parameterPosition = null,
|
||||||
ArrayParametersSerialization? arraySerialization = null,
|
ArrayParametersSerialization? arraySerialization = null,
|
||||||
int requestWeight = 1,
|
int requestWeight = 1,
|
||||||
@@ -232,7 +248,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
_logger.Log(LogLevel.Information, $"[{requestId}] Creating request for " + uri);
|
_logger.Log(LogLevel.Information, $"[{requestId}] Creating request for " + uri);
|
||||||
var paramsPosition = parameterPosition ?? ParameterPositions[method];
|
var paramsPosition = parameterPosition ?? ParameterPositions[method];
|
||||||
var request = ConstructRequest(uri, method, parameters?.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value), signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestId, additionalHeaders);
|
var request = ConstructRequest(uri, method, parameters?.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value), signed, paramsPosition, arraySerialization ?? this.arraySerialization, requestBodyFormat ?? this.requestBodyFormat, requestId, additionalHeaders);
|
||||||
|
|
||||||
string? paramString = "";
|
string? paramString = "";
|
||||||
if (paramsPosition == HttpMethodParameterPosition.InBody)
|
if (paramsPosition == HttpMethodParameterPosition.InBody)
|
||||||
@@ -261,9 +277,9 @@ namespace CryptoExchange.Net
|
|||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
bool expectedEmptyResponse)
|
bool expectedEmptyResponse)
|
||||||
{
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sw = Stopwatch.StartNew();
|
|
||||||
var response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
var response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
var statusCode = response.StatusCode;
|
var statusCode = response.StatusCode;
|
||||||
@@ -273,7 +289,7 @@ namespace CryptoExchange.Net
|
|||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
// If we have to manually parse error responses (can't rely on HttpStatusCode) we'll need to read the full
|
// If we have to manually parse error responses (can't rely on HttpStatusCode) we'll need to read the full
|
||||||
// response before being able to deserialize it into the resulting type since we don't know if it an error response or data
|
// response before being able to deserialize it into the resulting type since we don't know if its an error response or data
|
||||||
if (manualParseError)
|
if (manualParseError)
|
||||||
{
|
{
|
||||||
using var reader = new StreamReader(responseStream);
|
using var reader = new StreamReader(responseStream);
|
||||||
@@ -281,23 +297,22 @@ namespace CryptoExchange.Net
|
|||||||
responseLength ??= data.Length;
|
responseLength ??= data.Length;
|
||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
_logger.Log(LogLevel.Debug, $"[{request.RequestId}] Response received in {sw.ElapsedMilliseconds}ms{(OutputOriginalData ? (": " + data) : "")}");
|
|
||||||
|
|
||||||
if (!expectedEmptyResponse)
|
if (!expectedEmptyResponse)
|
||||||
{
|
{
|
||||||
// Validate if it is valid json. Sometimes other data will be returned, 502 error html pages for example
|
// Validate if it is valid json. Sometimes other data will be returned, 502 error html pages for example
|
||||||
var parseResult = ValidateJson(data);
|
var parseResult = ValidateJson(data);
|
||||||
if (!parseResult.Success)
|
if (!parseResult.Success)
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||||
|
|
||||||
// Let the library implementation see if it is an error response, and if so parse the error
|
// Let the library implementation see if it is an error response, and if so parse the error
|
||||||
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
||||||
if (error != null)
|
if (error != null)
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||||
|
|
||||||
// Not an error, so continue deserializing
|
// Not an error, so continue deserializing
|
||||||
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
|
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -306,16 +321,16 @@ namespace CryptoExchange.Net
|
|||||||
var parseResult = ValidateJson(data);
|
var parseResult = ValidateJson(data);
|
||||||
if (!parseResult.Success)
|
if (!parseResult.Success)
|
||||||
// Not empty, and not json
|
// Not empty, and not json
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
|
||||||
|
|
||||||
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
|
||||||
if (error != null)
|
if (error != null)
|
||||||
// Error response
|
// Error response
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty success response; okay
|
// Empty success response; okay
|
||||||
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, default);
|
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? data : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -326,7 +341,7 @@ namespace CryptoExchange.Net
|
|||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
|
|
||||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, null);
|
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success status code, and we don't have to check for errors. Continue deserializing directly from the stream
|
// Success status code, and we don't have to check for errors. Continue deserializing directly from the stream
|
||||||
@@ -334,7 +349,7 @@ namespace CryptoExchange.Net
|
|||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
|
|
||||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, responseLength, OutputOriginalData ? desResult.OriginalData : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error);
|
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, responseLength, OutputOriginalData ? desResult.OriginalData : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -342,7 +357,6 @@ namespace CryptoExchange.Net
|
|||||||
// Http status code indicates error
|
// Http status code indicates error
|
||||||
using var reader = new StreamReader(responseStream);
|
using var reader = new StreamReader(responseStream);
|
||||||
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
var data = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Error received in {sw.ElapsedMilliseconds}ms: {data}");
|
|
||||||
responseStream.Close();
|
responseStream.Close();
|
||||||
response.Close();
|
response.Close();
|
||||||
|
|
||||||
@@ -354,29 +368,26 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if (error.Code == null || error.Code == 0)
|
if (error.Code == null || error.Code == 0)
|
||||||
error.Code = (int)response.StatusCode;
|
error.Code = (int)response.StatusCode;
|
||||||
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, data.Length, data, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
|
return new WebCallResult<T>(statusCode, headers, sw.Elapsed, data.Length, data, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (HttpRequestException requestException)
|
catch (HttpRequestException requestException)
|
||||||
{
|
{
|
||||||
// Request exception, can't reach server for instance
|
// Request exception, can't reach server for instance
|
||||||
var exceptionInfo = requestException.ToLogString();
|
var exceptionInfo = requestException.ToLogString();
|
||||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request exception: " + exceptionInfo);
|
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo));
|
||||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo));
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException canceledException)
|
catch (OperationCanceledException canceledException)
|
||||||
{
|
{
|
||||||
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
|
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
|
||||||
{
|
{
|
||||||
// Cancellation token canceled by caller
|
// Cancellation token canceled by caller
|
||||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request canceled by cancellation token");
|
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError());
|
||||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Request timed out
|
// Request timed out
|
||||||
_logger.Log(LogLevel.Warning, $"[{request.RequestId}] Request timed out: " + canceledException.ToLogString());
|
return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"Request timed out"));
|
||||||
return new WebCallResult<T>(null, null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"[{request.RequestId}] Request timed out"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,6 +423,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="signed">Whether or not the request should be authenticated</param>
|
/// <param name="signed">Whether or not the request should be authenticated</param>
|
||||||
/// <param name="parameterPosition">Where the parameters should be placed</param>
|
/// <param name="parameterPosition">Where the parameters should be placed</param>
|
||||||
/// <param name="arraySerialization">How array parameters should be serialized</param>
|
/// <param name="arraySerialization">How array parameters should be serialized</param>
|
||||||
|
/// <param name="bodyFormat">Format of the body content</param>
|
||||||
/// <param name="requestId">Unique id of a request</param>
|
/// <param name="requestId">Unique id of a request</param>
|
||||||
/// <param name="additionalHeaders">Additional headers to send with the request</param>
|
/// <param name="additionalHeaders">Additional headers to send with the request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@@ -422,6 +434,7 @@ namespace CryptoExchange.Net
|
|||||||
bool signed,
|
bool signed,
|
||||||
HttpMethodParameterPosition parameterPosition,
|
HttpMethodParameterPosition parameterPosition,
|
||||||
ArrayParametersSerialization arraySerialization,
|
ArrayParametersSerialization arraySerialization,
|
||||||
|
RequestBodyFormat bodyFormat,
|
||||||
int requestId,
|
int requestId,
|
||||||
Dictionary<string, string>? additionalHeaders)
|
Dictionary<string, string>? additionalHeaders)
|
||||||
{
|
{
|
||||||
@@ -502,7 +515,7 @@ namespace CryptoExchange.Net
|
|||||||
|
|
||||||
if (parameterPosition == HttpMethodParameterPosition.InBody)
|
if (parameterPosition == HttpMethodParameterPosition.InBody)
|
||||||
{
|
{
|
||||||
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
var contentType = bodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
|
||||||
if (bodyParameters.Any())
|
if (bodyParameters.Any())
|
||||||
WriteParamBody(request, bodyParameters, contentType);
|
WriteParamBody(request, bodyParameters, contentType);
|
||||||
else
|
else
|
||||||
@@ -520,13 +533,13 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="contentType">The content type of the data</param>
|
/// <param name="contentType">The content type of the data</param>
|
||||||
protected virtual void WriteParamBody(IRequest request, SortedDictionary<string, object> parameters, string contentType)
|
protected virtual void WriteParamBody(IRequest request, SortedDictionary<string, object> parameters, string contentType)
|
||||||
{
|
{
|
||||||
if (requestBodyFormat == RequestBodyFormat.Json)
|
if (contentType == Constants.JsonContentHeader)
|
||||||
{
|
{
|
||||||
// Write the parameters as json in the body
|
// Write the parameters as json in the body
|
||||||
var stringData = JsonConvert.SerializeObject(parameters);
|
var stringData = JsonConvert.SerializeObject(parameters);
|
||||||
request.SetContent(stringData, contentType);
|
request.SetContent(stringData, contentType);
|
||||||
}
|
}
|
||||||
else if (requestBodyFormat == RequestBodyFormat.FormData)
|
else if (contentType == Constants.FormContentHeader)
|
||||||
{
|
{
|
||||||
// Write the parameters as form data in the body
|
// Write the parameters as form data in the body
|
||||||
var stringData = parameters.ToFormData();
|
var stringData = parameters.ToFormData();
|
||||||
@@ -580,14 +593,14 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
var timeSyncParams = GetTimeSyncInfo();
|
var timeSyncParams = GetTimeSyncInfo();
|
||||||
if (timeSyncParams == null)
|
if (timeSyncParams == null)
|
||||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||||
|
|
||||||
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
|
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval))
|
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval))
|
||||||
{
|
{
|
||||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var localTime = DateTime.UtcNow;
|
var localTime = DateTime.UtcNow;
|
||||||
@@ -616,7 +629,7 @@ namespace CryptoExchange.Net
|
|||||||
timeSyncParams.TimeSyncState.Semaphore.Release();
|
timeSyncParams.TimeSyncState.Semaphore.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, true, null);
|
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, true, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ namespace CryptoExchange.Net
|
|||||||
if (!authenticated || socket.Authenticated)
|
if (!authenticated || socket.Authenticated)
|
||||||
return new CallResult<bool>(true);
|
return new CallResult<bool>(true);
|
||||||
|
|
||||||
_logger.Log(LogLevel.Debug, $"Attempting to authenticate {socket.SocketId}");
|
_logger.Log(LogLevel.Debug, $"Socket {socket.SocketId} Attempting to authenticate");
|
||||||
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
|
var result = await AuthenticateSocketAsync(socket).ConfigureAwait(false);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
@@ -414,6 +414,7 @@ namespace CryptoExchange.Net
|
|||||||
return new CallResult<bool>(result.Error);
|
return new CallResult<bool>(result.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.Log(LogLevel.Debug, $"Socket {socket.SocketId} authenticated");
|
||||||
socket.Authenticated = true;
|
socket.Authenticated = true;
|
||||||
return new CallResult<bool>(true);
|
return new CallResult<bool>(true);
|
||||||
}
|
}
|
||||||
@@ -511,7 +512,7 @@ namespace CryptoExchange.Net
|
|||||||
if (typeof(T) == typeof(string))
|
if (typeof(T) == typeof(string))
|
||||||
{
|
{
|
||||||
var stringData = (T)Convert.ChangeType(messageEvent.JsonData.ToString(), typeof(T));
|
var stringData = (T)Convert.ChangeType(messageEvent.JsonData.ToString(), typeof(T));
|
||||||
dataHandler(new DataEvent<T>(stringData, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
dataHandler(new DataEvent<T>(stringData, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp, null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +523,7 @@ namespace CryptoExchange.Net
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataHandler(new DataEvent<T>(desResult.Data, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp));
|
dataHandler(new DataEvent<T>(desResult.Data, null, OutputOriginalData ? messageEvent.OriginalData : null, messageEvent.ReceivedTimestamp, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscription = request == null
|
var subscription = request == null
|
||||||
@@ -562,7 +563,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connection"></param>
|
/// <param name="connection"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual Task<Uri?> GetReconnectUriAsync(SocketConnection connection)
|
protected internal virtual Task<Uri?> GetReconnectUriAsync(SocketConnection connection)
|
||||||
{
|
{
|
||||||
return Task.FromResult<Uri?>(connection.ConnectionUri);
|
return Task.FromResult<Uri?>(connection.ConnectionUri);
|
||||||
}
|
}
|
||||||
@@ -572,7 +573,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The original request</param>
|
/// <param name="request">The original request</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual Task<CallResult<object>> RevitalizeRequestAsync(object request)
|
protected internal virtual Task<CallResult<object>> RevitalizeRequestAsync(object request)
|
||||||
{
|
{
|
||||||
return Task.FromResult(new CallResult<object>(request));
|
return Task.FromResult(new CallResult<object>(request));
|
||||||
}
|
}
|
||||||
@@ -682,7 +683,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="identifier">Identifier for the periodic send</param>
|
/// <param name="identifier">Identifier for the periodic send</param>
|
||||||
/// <param name="interval">How often</param>
|
/// <param name="interval">How often</param>
|
||||||
/// <param name="objGetter">Method returning the object to send</param>
|
/// <param name="objGetter">Method returning the object to send</param>
|
||||||
public virtual void SendPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, object> objGetter)
|
protected virtual void SendPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, object> objGetter)
|
||||||
{
|
{
|
||||||
if (objGetter == null)
|
if (objGetter == null)
|
||||||
throw new ArgumentNullException(nameof(objGetter));
|
throw new ArgumentNullException(nameof(objGetter));
|
||||||
|
|||||||
@@ -40,11 +40,14 @@ namespace CryptoExchange.Net.Converters
|
|||||||
case "yes":
|
case "yes":
|
||||||
case "y":
|
case "y":
|
||||||
case "1":
|
case "1":
|
||||||
|
case "on":
|
||||||
return true;
|
return true;
|
||||||
case "false":
|
case "false":
|
||||||
case "no":
|
case "no":
|
||||||
case "n":
|
case "n":
|
||||||
case "0":
|
case "0":
|
||||||
|
case "off":
|
||||||
|
case "-1":
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Kraken.Net.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converter for serializing decimal values as string
|
||||||
|
/// </summary>
|
||||||
|
public class DecimalStringWriterConverter : JsonConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanRead => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanConvert(Type objectType) => objectType == typeof(decimal) || objectType == typeof(decimal?);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => writer.WriteValue(((decimal?)value)?.ToString(CultureInfo.InvariantCulture) ?? null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
public class EnumConverter : JsonConverter
|
public class EnumConverter : JsonConverter
|
||||||
{
|
{
|
||||||
private bool _warnOnMissingEntry = true;
|
private bool _warnOnMissingEntry = true;
|
||||||
|
private bool _writeAsInt;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -22,9 +23,11 @@ namespace CryptoExchange.Net.Converters
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="writeAsInt"></param>
|
||||||
/// <param name="warnOnMissingEntry"></param>
|
/// <param name="warnOnMissingEntry"></param>
|
||||||
public EnumConverter(bool warnOnMissingEntry)
|
public EnumConverter(bool writeAsInt, bool warnOnMissingEntry)
|
||||||
{
|
{
|
||||||
|
_writeAsInt = writeAsInt;
|
||||||
_warnOnMissingEntry = warnOnMissingEntry;
|
_warnOnMissingEntry = warnOnMissingEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ namespace CryptoExchange.Net.Converters
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType.IsEnum;
|
return objectType.IsEnum || Nullable.GetUnderlyingType(objectType)?.IsEnum == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -158,8 +161,15 @@ namespace CryptoExchange.Net.Converters
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var stringValue = GetString(value.GetType(), value);
|
if (!_writeAsInt)
|
||||||
writer.WriteValue(stringValue);
|
{
|
||||||
|
var stringValue = GetString(value.GetType(), value);
|
||||||
|
writer.WriteValue(stringValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteValue((int)value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Serializer options
|
||||||
|
/// </summary>
|
||||||
|
public static class SerializerOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Json serializer settings which includes the EnumConverter, DateTimeConverter and BoolConverter
|
||||||
|
/// </summary>
|
||||||
|
public static JsonSerializerSettings WithConverters => new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
|
Culture = CultureInfo.InvariantCulture,
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
new EnumConverter(),
|
||||||
|
new DateTimeConverter(),
|
||||||
|
new BoolConverter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default json serializer settings
|
||||||
|
/// </summary>
|
||||||
|
public static JsonSerializerSettings Default => new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
|
Culture = CultureInfo.InvariantCulture
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,16 +6,16 @@
|
|||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>A base package for implementing cryptocurrency API's</Description>
|
<Description>A base package for implementing cryptocurrency API's</Description>
|
||||||
<PackageVersion>6.1.4</PackageVersion>
|
<PackageVersion>6.2.3</PackageVersion>
|
||||||
<AssemblyVersion>6.1.4</AssemblyVersion>
|
<AssemblyVersion>6.2.3</AssemblyVersion>
|
||||||
<FileVersion>6.1.4</FileVersion>
|
<FileVersion>6.2.3</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
|
<RepositoryUrl>https://github.com/JKorf/CryptoExchange.Net.git</RepositoryUrl>
|
||||||
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/JKorf/CryptoExchange.Net</PackageProjectUrl>
|
||||||
<NeutralLanguage>en</NeutralLanguage>
|
<NeutralLanguage>en</NeutralLanguage>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<PackageReleaseNotes>6.1.4 - Added BoolConverter, Added parameter for logging warning message on missing enum entry to EnumConverter</PackageReleaseNotes>
|
<PackageReleaseNotes>6.2.3 - Fixed requestBodyFormat parameter handling</PackageReleaseNotes>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>10.0</LangVersion>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace CryptoExchange.Net
|
namespace CryptoExchange.Net
|
||||||
{
|
{
|
||||||
@@ -8,6 +9,8 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ExchangeHelpers
|
public static class ExchangeHelpers
|
||||||
{
|
{
|
||||||
|
private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last used id, use NextId() to get the next id and up this
|
/// The last used id, use NextId() to get the next id and up this
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -140,5 +143,43 @@ namespace CryptoExchange.Net
|
|||||||
return _lastId;
|
return _lastId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a random string of specified length
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">Length of the random string</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string RandomString(int length)
|
||||||
|
{
|
||||||
|
var randomChars = new char[length];
|
||||||
|
|
||||||
|
#if NETSTANDARD2_1_OR_GREATER
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
randomChars[i] = _allowedRandomChars[RandomNumberGenerator.GetInt32(0, _allowedRandomChars.Length)];
|
||||||
|
#else
|
||||||
|
var random = new Random();
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
randomChars[i] = _allowedRandomChars[random.Next(0, _allowedRandomChars.Length)];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return new string(randomChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a random string of specified length
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">The initial string</param>
|
||||||
|
/// <param name="totalLength">Total length of the resulting string</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string AppendRandomString(string source, int totalLength)
|
||||||
|
{
|
||||||
|
if (totalLength < source.Length)
|
||||||
|
throw new ArgumentException("Total length smaller than source string length", nameof(totalLength));
|
||||||
|
|
||||||
|
if (totalLength == source.Length)
|
||||||
|
return source;
|
||||||
|
|
||||||
|
return source + RandomString(totalLength - source.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,5 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// Total amount of requests made with this API client
|
/// Total amount of requests made with this API client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int TotalRequestsMade { get; set; }
|
int TotalRequestsMade { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get time offset for an API client. Return null if time syncing shouldnt/cant be done
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
TimeSpan? GetTimeOffset();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get time sync info for an API client. Return null if time syncing shouldnt/cant be done
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
TimeSyncInfo? GetTimeSyncInfo();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,14 +27,6 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// The factory for creating sockets. Used for unit testing
|
/// The factory for creating sockets. Used for unit testing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IWebsocketFactory SocketFactory { get; set; }
|
IWebsocketFactory SocketFactory { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the url to reconnect to after losing a connection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connection"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Uri?> GetReconnectUriAsync(SocketConnection connection);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Log the current state of connections and subscriptions
|
/// Log the current state of connections and subscriptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -45,19 +37,6 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task ReconnectAsync();
|
Task ReconnectAsync();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the original request to send when the connection is restored after disconnecting. Can be used to update an authentication token for example.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">The original request</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<CallResult<object>> RevitalizeRequestAsync(object request);
|
|
||||||
/// <summary>
|
|
||||||
/// Periodically sends data over a socket connection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="identifier">Identifier for the periodic send</param>
|
|
||||||
/// <param name="interval">How often</param>
|
|
||||||
/// <param name="objGetter">Method returning the object to send</param>
|
|
||||||
void SendPeriodic(string identifier, TimeSpan interval, Func<SocketConnection, object> objGetter);
|
|
||||||
/// <summary>
|
|
||||||
/// Unsubscribe all subscriptions
|
/// Unsubscribe all subscriptions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|||||||
@@ -186,6 +186,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request id
|
||||||
|
/// </summary>
|
||||||
|
public int? RequestId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The url which was requested
|
/// The url which was requested
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -217,6 +222,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="code"></param>
|
/// <param name="code"></param>
|
||||||
/// <param name="responseHeaders"></param>
|
/// <param name="responseHeaders"></param>
|
||||||
/// <param name="responseTime"></param>
|
/// <param name="responseTime"></param>
|
||||||
|
/// <param name="requestId"></param>
|
||||||
/// <param name="requestUrl"></param>
|
/// <param name="requestUrl"></param>
|
||||||
/// <param name="requestBody"></param>
|
/// <param name="requestBody"></param>
|
||||||
/// <param name="requestMethod"></param>
|
/// <param name="requestMethod"></param>
|
||||||
@@ -226,6 +232,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
HttpStatusCode? code,
|
HttpStatusCode? code,
|
||||||
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
|
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
|
||||||
TimeSpan? responseTime,
|
TimeSpan? responseTime,
|
||||||
|
int? requestId,
|
||||||
string? requestUrl,
|
string? requestUrl,
|
||||||
string? requestBody,
|
string? requestBody,
|
||||||
HttpMethod? requestMethod,
|
HttpMethod? requestMethod,
|
||||||
@@ -235,6 +242,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
ResponseStatusCode = code;
|
ResponseStatusCode = code;
|
||||||
ResponseHeaders = responseHeaders;
|
ResponseHeaders = responseHeaders;
|
||||||
ResponseTime = responseTime;
|
ResponseTime = responseTime;
|
||||||
|
RequestId = requestId;
|
||||||
|
|
||||||
RequestUrl = requestUrl;
|
RequestUrl = requestUrl;
|
||||||
RequestBody = requestBody;
|
RequestBody = requestBody;
|
||||||
@@ -255,7 +263,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public WebCallResult AsError(Error error)
|
public WebCallResult AsError(Error error)
|
||||||
{
|
{
|
||||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -281,6 +289,11 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request id
|
||||||
|
/// </summary>
|
||||||
|
public int? RequestId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The url which was requested
|
/// The url which was requested
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -319,6 +332,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <param name="responseTime"></param>
|
/// <param name="responseTime"></param>
|
||||||
/// <param name="responseLength"></param>
|
/// <param name="responseLength"></param>
|
||||||
/// <param name="originalData"></param>
|
/// <param name="originalData"></param>
|
||||||
|
/// <param name="requestId"></param>
|
||||||
/// <param name="requestUrl"></param>
|
/// <param name="requestUrl"></param>
|
||||||
/// <param name="requestBody"></param>
|
/// <param name="requestBody"></param>
|
||||||
/// <param name="requestMethod"></param>
|
/// <param name="requestMethod"></param>
|
||||||
@@ -331,6 +345,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
TimeSpan? responseTime,
|
TimeSpan? responseTime,
|
||||||
long? responseLength,
|
long? responseLength,
|
||||||
string? originalData,
|
string? originalData,
|
||||||
|
int? requestId,
|
||||||
string? requestUrl,
|
string? requestUrl,
|
||||||
string? requestBody,
|
string? requestBody,
|
||||||
HttpMethod? requestMethod,
|
HttpMethod? requestMethod,
|
||||||
@@ -343,6 +358,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
ResponseTime = responseTime;
|
ResponseTime = responseTime;
|
||||||
ResponseLength = responseLength;
|
ResponseLength = responseLength;
|
||||||
|
|
||||||
|
RequestId = requestId;
|
||||||
RequestUrl = requestUrl;
|
RequestUrl = requestUrl;
|
||||||
RequestBody = requestBody;
|
RequestBody = requestBody;
|
||||||
RequestHeaders = requestHeaders;
|
RequestHeaders = requestHeaders;
|
||||||
@@ -355,7 +371,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new WebCallResult AsDataless()
|
public new WebCallResult AsDataless()
|
||||||
{
|
{
|
||||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy as a dataless result
|
/// Copy as a dataless result
|
||||||
@@ -363,14 +379,14 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new WebCallResult AsDatalessError(Error error)
|
public new WebCallResult AsDatalessError(Error error)
|
||||||
{
|
{
|
||||||
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new error result
|
/// Create a new error result
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="error">The error</param>
|
/// <param name="error">The error</param>
|
||||||
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, default, error) { }
|
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, default, error) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy the WebCallResult to a new data type
|
/// Copy the WebCallResult to a new data type
|
||||||
@@ -380,7 +396,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new WebCallResult<K> As<K>([AllowNull] K data)
|
public new WebCallResult<K> As<K>([AllowNull] K data)
|
||||||
{
|
{
|
||||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
|
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -391,7 +407,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public new WebCallResult<K> AsError<K>(Error error)
|
public new WebCallResult<K> AsError<K>(Error error)
|
||||||
{
|
{
|
||||||
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
|
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -124,4 +124,19 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Closest
|
Closest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of the update
|
||||||
|
/// </summary>
|
||||||
|
public enum SocketUpdateType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A update
|
||||||
|
/// </summary>
|
||||||
|
Update,
|
||||||
|
/// <summary>
|
||||||
|
/// A snapshot, generally send at the start of the connection
|
||||||
|
/// </summary>
|
||||||
|
Snapshot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Code != null ? $"{Code}: {Message} {Data}" : $"{Message} {Data}";
|
return Code != null ? $"[{GetType().Name}] {Code}: {Message} {Data}" : $"[{GetType().Name}] {Message} {Data}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
using CryptoExchange.Net.Attributes;
|
||||||
|
using CryptoExchange.Net.Converters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters collection
|
||||||
|
/// </summary>
|
||||||
|
public class ParameterCollection : Dictionary<string, object>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add an optional parameter. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptional(string key, object? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a decimal value as string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddString(string key, decimal value)
|
||||||
|
{
|
||||||
|
Add(key, value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a decimal value as string. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalString(string key, decimal? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a int value as string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddString(string key, int value)
|
||||||
|
{
|
||||||
|
Add(key, value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a int value as string. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalString(string key, int? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a long value as string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddString(string key, long value)
|
||||||
|
{
|
||||||
|
Add(key, value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a long value as string. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalString(string key, long? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as milliseconds timestamp
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddMilliseconds(string key, DateTime value)
|
||||||
|
{
|
||||||
|
Add(key, DateTimeConverter.ConvertToMilliseconds(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as milliseconds timestamp. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalMilliseconds(string key, DateTime? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, DateTimeConverter.ConvertToMilliseconds(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as milliseconds timestamp
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddMillisecondsString(string key, DateTime value)
|
||||||
|
{
|
||||||
|
Add(key, DateTimeConverter.ConvertToMilliseconds(value).Value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as milliseconds timestamp. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalMillisecondsString(string key, DateTime? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, DateTimeConverter.ConvertToMilliseconds(value).Value.ToString(CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as seconds timestamp
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddSeconds(string key, DateTime value)
|
||||||
|
{
|
||||||
|
Add(key, DateTimeConverter.ConvertToSeconds(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a datetime value as seconds timestamp. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalSeconds(string key, DateTime? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, DateTimeConverter.ConvertToSeconds(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddEnum<T>(string key, T value)
|
||||||
|
{
|
||||||
|
Add(key, EnumConverter.GetString(value)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />. Not added if value is null
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public void AddOptionalEnum<T>(string key, T? value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
Add(key, EnumConverter.GetString(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -464,7 +464,7 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
{
|
{
|
||||||
var pbList = _processBuffer.ToList();
|
var pbList = _processBuffer.ToList();
|
||||||
if (pbList.Count > 0)
|
if (pbList.Count > 0)
|
||||||
_logger.Log(LogLevel.Debug, $"Processing {pbList.Count} buffered updates");
|
_logger.Log(LogLevel.Debug, $"{Id} Processing {pbList.Count} buffered updates");
|
||||||
|
|
||||||
foreach (var bufferEntry in pbList)
|
foreach (var bufferEntry in pbList)
|
||||||
{
|
{
|
||||||
@@ -661,7 +661,7 @@ namespace CryptoExchange.Net.OrderBook
|
|||||||
|
|
||||||
if (_stopProcessing)
|
if (_stopProcessing)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Trace, "Skipping message because of resubscribing");
|
_logger.Log(LogLevel.Trace, $"{Id} Skipping message because of resubscribing");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using CryptoExchange.Net.Objects;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.Sockets
|
namespace CryptoExchange.Net.Sockets
|
||||||
{
|
{
|
||||||
@@ -23,35 +24,23 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? OriginalData { get; set; }
|
public string? OriginalData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of update
|
||||||
|
/// </summary>
|
||||||
|
public SocketUpdateType? UpdateType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The received data deserialized into an object
|
/// The received data deserialized into an object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public T Data { get; set; }
|
public T Data { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
internal DataEvent(T data, string? topic, string? originalData, DateTime timestamp, SocketUpdateType? updateType)
|
||||||
/// Ctor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data"></param>
|
|
||||||
/// <param name="timestamp"></param>
|
|
||||||
public DataEvent(T data, DateTime timestamp)
|
|
||||||
{
|
|
||||||
Data = data;
|
|
||||||
Timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal DataEvent(T data, string? topic, DateTime timestamp)
|
|
||||||
{
|
|
||||||
Data = data;
|
|
||||||
Topic = topic;
|
|
||||||
Timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal DataEvent(T data, string? topic, string? originalData, DateTime timestamp)
|
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = data;
|
||||||
Topic = topic;
|
Topic = topic;
|
||||||
OriginalData = originalData;
|
OriginalData = originalData;
|
||||||
Timestamp = timestamp;
|
Timestamp = timestamp;
|
||||||
|
UpdateType = updateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -62,7 +51,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public DataEvent<K> As<K>(K data)
|
public DataEvent<K> As<K>(K data)
|
||||||
{
|
{
|
||||||
return new DataEvent<K>(data, Topic, OriginalData, Timestamp);
|
return new DataEvent<K>(data, Topic, OriginalData, Timestamp, UpdateType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -74,7 +63,20 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public DataEvent<K> As<K>(K data, string? topic)
|
public DataEvent<K> As<K>(K data, string? topic)
|
||||||
{
|
{
|
||||||
return new DataEvent<K>(data, topic, OriginalData, Timestamp);
|
return new DataEvent<K>(data, topic, OriginalData, Timestamp, UpdateType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new DataEvent with data in the from of type K based on the current DataEvent. OriginalData and Timestamp will be copied over
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="K">The type of the new data</typeparam>
|
||||||
|
/// <param name="data">The new data</param>
|
||||||
|
/// <param name="topic">The new topic</param>
|
||||||
|
/// <param name="updateType">The type of update</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public DataEvent<K> As<K>(K data, string? topic, SocketUpdateType updateType)
|
||||||
|
{
|
||||||
|
return new DataEvent<K>(data, topic, OriginalData, Timestamp, updateType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
var reconnectSuccessful = await ProcessReconnectAsync().ConfigureAwait(false);
|
var reconnectSuccessful = await ProcessReconnectAsync().ConfigureAwait(false);
|
||||||
if (!reconnectSuccessful)
|
if (!reconnectSuccessful)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Warning, $"Failed reconnect processing: {reconnectSuccessful.Error}, reconnecting again");
|
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Failed reconnect processing: {reconnectSuccessful.Error}, reconnecting again");
|
||||||
await _socket.ReconnectAsync().ConfigureAwait(false);
|
await _socket.ReconnectAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -348,7 +348,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
// Answer to a timed out request, unsub if it is a subscription request
|
// Answer to a timed out request, unsub if it is a subscription request
|
||||||
if (pendingRequest.Subscription != null)
|
if (pendingRequest.Subscription != null)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Warning, "Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Received subscription info after request timed out; unsubscribing. Consider increasing the RequestTimeout");
|
||||||
_ = ApiClient.UnsubscribeAsync(this, pendingRequest.Subscription).ConfigureAwait(false);
|
_ = ApiClient.UnsubscribeAsync(this, pendingRequest.Subscription).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -714,7 +714,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
var result = await ApiClient.RevitalizeRequestAsync(subscription.Request!).ConfigureAwait(false);
|
var result = await ApiClient.RevitalizeRequestAsync(subscription.Request!).ConfigureAwait(false);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Warning, "Failed request revitalization: " + result.Error);
|
_logger.Log(LogLevel.Warning, $"Socket {SocketId} Failed request revitalization: " + result.Error);
|
||||||
return result.As<bool>(false);
|
return result.As<bool>(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="9.0.1" />
|
<PackageReference Include="Binance.Net" Version="9.1.5" />
|
||||||
<PackageReference Include="Bitfinex.Net" Version="6.0.0" />
|
<PackageReference Include="Bitfinex.Net" Version="7.0.4" />
|
||||||
<PackageReference Include="Bittrex.Net" Version="8.0.0" />
|
<PackageReference Include="Bittrex.Net" Version="8.0.3" />
|
||||||
<PackageReference Include="Bybit.Net" Version="3.0.0" />
|
<PackageReference Include="Bybit.Net" Version="3.2.1" />
|
||||||
<PackageReference Include="CoinEx.Net" Version="6.0.0" />
|
<PackageReference Include="CoinEx.Net" Version="6.0.3" />
|
||||||
<PackageReference Include="Huobi.Net" Version="5.0.0" />
|
<PackageReference Include="Huobi.Net" Version="5.0.3" />
|
||||||
<PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
|
<PackageReference Include="JK.Bitget.Net" Version="1.0.0" />
|
||||||
<PackageReference Include="Kucoin.Net" Version="5.0.0" />
|
<PackageReference Include="JK.OKX.Net" Version="1.4.2" />
|
||||||
|
<PackageReference Include="KrakenExchange.Net" Version="4.1.5" />
|
||||||
|
<PackageReference Include="Kucoin.Net" Version="5.0.5" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@inject IBinanceRestClient binanceClient
|
@inject IBinanceRestClient binanceClient
|
||||||
@inject IBitfinexRestClient bitfinexClient
|
@inject IBitfinexRestClient bitfinexClient
|
||||||
|
@inject IBitgetRestClient bitgetClient
|
||||||
@inject IBittrexRestClient bittrexClient
|
@inject IBittrexRestClient bittrexClient
|
||||||
@inject IBybitRestClient bybitClient
|
@inject IBybitRestClient bybitClient
|
||||||
@inject ICoinExRestClient coinexClient
|
@inject ICoinExRestClient coinexClient
|
||||||
@inject IHuobiRestClient huobiClient
|
@inject IHuobiRestClient huobiClient
|
||||||
@inject IKrakenRestClient krakenClient
|
@inject IKrakenRestClient krakenClient
|
||||||
@inject IKucoinRestClient kucoinClient
|
@inject IKucoinRestClient kucoinClient
|
||||||
|
@inject IOKXRestClient okxClient
|
||||||
|
|
||||||
<h3>BTC-USD prices:</h3>
|
<h3>BTC-USD prices:</h3>
|
||||||
@foreach(var price in _prices.OrderBy(p => p.Key))
|
@foreach(var price in _prices.OrderBy(p => p.Key))
|
||||||
@@ -21,12 +23,14 @@
|
|||||||
{
|
{
|
||||||
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
|
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
|
||||||
|
var bitgetTask = bitgetClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT_SPBL");
|
||||||
var bittrexTask = bittrexClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
var bittrexTask = bittrexClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
||||||
var bybitTask = bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
|
var bybitTask = bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
|
||||||
var coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var coinexTask = coinexClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
var huobiTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
var huobiTask = huobiClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
||||||
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 okxTask = okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
|
|
||||||
await Task.WhenAll(binanceTask, bitfinexTask, bittrexTask, bybitTask, coinexTask, huobiTask, krakenTask, kucoinTask);
|
await Task.WhenAll(binanceTask, bitfinexTask, bittrexTask, bybitTask, coinexTask, huobiTask, krakenTask, kucoinTask);
|
||||||
|
|
||||||
@@ -36,6 +40,9 @@
|
|||||||
if (bitfinexTask.Result.Success)
|
if (bitfinexTask.Result.Success)
|
||||||
_prices.Add("Bitfinex", bitfinexTask.Result.Data.LastPrice);
|
_prices.Add("Bitfinex", bitfinexTask.Result.Data.LastPrice);
|
||||||
|
|
||||||
|
if (bitgetTask.Result.Success)
|
||||||
|
_prices.Add("Bitget", bitgetTask.Result.Data.ClosePrice);
|
||||||
|
|
||||||
if (bittrexTask.Result.Success)
|
if (bittrexTask.Result.Success)
|
||||||
_prices.Add("Bittrex", bittrexTask.Result.Data.LastPrice);
|
_prices.Add("Bittrex", bittrexTask.Result.Data.LastPrice);
|
||||||
|
|
||||||
@@ -53,6 +60,9 @@
|
|||||||
|
|
||||||
if (kucoinTask.Result.Success)
|
if (kucoinTask.Result.Success)
|
||||||
_prices.Add("Kucoin", kucoinTask.Result.Data.LastPrice ?? 0);
|
_prices.Add("Kucoin", kucoinTask.Result.Data.LastPrice ?? 0);
|
||||||
|
|
||||||
|
if (okxTask.Result.Success)
|
||||||
|
_prices.Add("OKX", okxTask.Result.Data.LastPrice ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
@page "/LiveData"
|
@page "/LiveData"
|
||||||
@inject IBinanceSocketClient binanceSocketClient
|
@inject IBinanceSocketClient binanceSocketClient
|
||||||
@inject IBitfinexSocketClient bitfinexSocketClient
|
@inject IBitfinexSocketClient bitfinexSocketClient
|
||||||
|
@inject IBitgetSocketClient bitgetSocketClient
|
||||||
@inject IBittrexSocketClient bittrexSocketClient
|
@inject IBittrexSocketClient bittrexSocketClient
|
||||||
@inject IBybitSocketClient bybitSocketClient
|
@inject IBybitSocketClient bybitSocketClient
|
||||||
@inject ICoinExSocketClient coinExSocketClient
|
@inject ICoinExSocketClient coinExSocketClient
|
||||||
@inject IHuobiSocketClient huobiSocketClient
|
@inject IHuobiSocketClient huobiSocketClient
|
||||||
@inject IKrakenSocketClient krakenSocketClient
|
@inject IKrakenSocketClient krakenSocketClient
|
||||||
@inject IKucoinSocketClient kucoinSocketClient
|
@inject IKucoinSocketClient kucoinSocketClient
|
||||||
|
@inject IOKXSocketClient okxSocketClient
|
||||||
@using System.Collections.Concurrent
|
@using System.Collections.Concurrent
|
||||||
@using CryptoExchange.Net.Objects
|
@using CryptoExchange.Net.Objects
|
||||||
@using CryptoExchange.Net.Sockets
|
@using CryptoExchange.Net.Sockets
|
||||||
@@ -28,12 +30,14 @@
|
|||||||
{
|
{
|
||||||
binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
|
binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
|
||||||
bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
|
bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
|
||||||
|
bitgetSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Bitget", data.Data.LastPrice)),
|
||||||
bittrexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", data.Data.LastPrice)),
|
bittrexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Bittrex", 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)),
|
||||||
huobiSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)),
|
huobiSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ethbtc", data => UpdateData("Huobi", data.Data.ClosePrice ?? 0)),
|
||||||
krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)),
|
krakenSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH/XBT", data => UpdateData("Kraken", data.Data.LastTrade.Price)),
|
||||||
kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
|
kucoinSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("Kucoin", data.Data.LastPrice ?? 0)),
|
||||||
|
okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("OKX", data.LastPrice ?? 0)),
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using System.Timers
|
@using System.Timers
|
||||||
@using Binance.Net.Interfaces
|
@using Binance.Net.Interfaces
|
||||||
@using Bitfinex.Net.Interfaces
|
@using Bitfinex.Net.Interfaces
|
||||||
|
@using Bitget.Net.Interfaces;
|
||||||
@using Bittrex.Net.Interfaces
|
@using Bittrex.Net.Interfaces
|
||||||
@using Bybit.Net.Interfaces
|
@using Bybit.Net.Interfaces
|
||||||
@using CoinEx.Net.Interfaces
|
@using CoinEx.Net.Interfaces
|
||||||
@@ -11,14 +12,17 @@
|
|||||||
@using Kraken.Net.Interfaces
|
@using Kraken.Net.Interfaces
|
||||||
@using Kucoin.Net.Clients
|
@using Kucoin.Net.Clients
|
||||||
@using Kucoin.Net.Interfaces
|
@using Kucoin.Net.Interfaces
|
||||||
|
@using OKX.Net.Interfaces;
|
||||||
@inject IBinanceOrderBookFactory binanceFactory
|
@inject IBinanceOrderBookFactory binanceFactory
|
||||||
@inject IBitfinexOrderBookFactory bitfinexFactory
|
@inject IBitfinexOrderBookFactory bitfinexFactory
|
||||||
|
@inject IBitgetOrderBookFactory bitgetFactory
|
||||||
@inject IBittrexOrderBookFactory bittrexFactory
|
@inject IBittrexOrderBookFactory bittrexFactory
|
||||||
@inject IBybitOrderBookFactory bybitFactory
|
@inject IBybitOrderBookFactory bybitFactory
|
||||||
@inject ICoinExOrderBookFactory coinExFactory
|
@inject ICoinExOrderBookFactory coinExFactory
|
||||||
@inject IHuobiOrderBookFactory huobiFactory
|
@inject IHuobiOrderBookFactory huobiFactory
|
||||||
@inject IKrakenOrderBookFactory krakenFactory
|
@inject IKrakenOrderBookFactory krakenFactory
|
||||||
@inject IKucoinOrderBookFactory kucoinFactory
|
@inject IKucoinOrderBookFactory kucoinFactory
|
||||||
|
@inject IOKXOrderBookFactory okxFactory
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
<h3>ETH-BTC books, live updates:</h3>
|
<h3>ETH-BTC books, live updates:</h3>
|
||||||
@@ -54,12 +58,14 @@
|
|||||||
{
|
{
|
||||||
{ "Binance", binanceFactory.CreateSpot("ETHBTC") },
|
{ "Binance", binanceFactory.CreateSpot("ETHBTC") },
|
||||||
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") },
|
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") },
|
||||||
|
{ "Bitget", bitgetFactory.CreateSpot("ETHBTC") },
|
||||||
{ "Bittrex", bittrexFactory.Create("ETH-BTC") },
|
{ "Bittrex", bittrexFactory.Create("ETH-BTC") },
|
||||||
{ "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) },
|
{ "Bybit", bybitFactory.Create("ETHBTC", Bybit.Net.Enums.Category.Spot) },
|
||||||
{ "CoinEx", coinExFactory.CreateSpot("ETHBTC") },
|
{ "CoinEx", coinExFactory.CreateSpot("ETHBTC") },
|
||||||
{ "Huobi", huobiFactory.CreateSpot("ethbtc") },
|
{ "Huobi", huobiFactory.CreateSpot("ethbtc") },
|
||||||
{ "Kraken", krakenFactory.CreateSpot("ETH/XBT") },
|
{ "Kraken", krakenFactory.CreateSpot("ETH/XBT") },
|
||||||
{ "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
|
{ "Kucoin", kucoinFactory.CreateSpot("ETH-BTC") },
|
||||||
|
{ "OKX", okxFactory.Create("ETH-BTC") },
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAll(_books.Select(b => b.Value.StartAsync()));
|
await Task.WhenAll(_books.Select(b => b.Value.StartAsync()));
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
@page "/SpotClient"
|
@page "/SpotClient"
|
||||||
@inject IBinanceRestClient binanceClient
|
@inject IBinanceRestClient binanceClient
|
||||||
@inject IBitfinexRestClient bitfinexClient
|
@inject IBitfinexRestClient bitfinexClient
|
||||||
|
@inject IBitgetRestClient bitgetClient
|
||||||
@inject IBittrexRestClient bittrexClient
|
@inject IBittrexRestClient bittrexClient
|
||||||
@inject IBybitRestClient bybitClient
|
@inject IBybitRestClient bybitClient
|
||||||
@inject ICoinExRestClient coinexClient
|
@inject ICoinExRestClient coinexClient
|
||||||
@inject IHuobiRestClient huobiClient
|
@inject IHuobiRestClient huobiClient
|
||||||
@inject IKrakenRestClient krakenClient
|
@inject IKrakenRestClient krakenClient
|
||||||
@inject IKucoinRestClient kucoinClient
|
@inject IKucoinRestClient kucoinClient
|
||||||
|
@inject IOKXRestClient okxClient
|
||||||
@using Binance.Net.Clients.SpotApi
|
@using Binance.Net.Clients.SpotApi
|
||||||
@using Bitfinex.Net.Clients.SpotApi
|
@using Bitfinex.Net.Clients.SpotApi
|
||||||
@using Bittrex.Net.Clients.SpotApi
|
@using Bittrex.Net.Clients.SpotApi
|
||||||
|
@using Bitget.Net.Clients.SpotApi
|
||||||
@using Bybit.Net.Clients.SpotApi
|
@using Bybit.Net.Clients.SpotApi
|
||||||
@using CoinEx.Net.Clients.SpotApi
|
@using CoinEx.Net.Clients.SpotApi
|
||||||
@using CryptoExchange.Net.Interfaces
|
@using CryptoExchange.Net.Interfaces
|
||||||
@@ -17,6 +20,7 @@
|
|||||||
@using Huobi.Net.Clients.SpotApi
|
@using Huobi.Net.Clients.SpotApi
|
||||||
@using Kraken.Net.Clients.SpotApi
|
@using Kraken.Net.Clients.SpotApi
|
||||||
@using Kucoin.Net.Clients.SpotApi
|
@using Kucoin.Net.Clients.SpotApi
|
||||||
|
@using OKX.Net.Clients.UnifiedApi
|
||||||
|
|
||||||
<h3>ETH-BTC prices:</h3>
|
<h3>ETH-BTC prices:</h3>
|
||||||
@foreach(var price in _prices.OrderBy(p => p.Key))
|
@foreach(var price in _prices.OrderBy(p => p.Key))
|
||||||
@@ -34,12 +38,14 @@
|
|||||||
|
|
||||||
binanceClient.SpotApi.CommonSpotClient,
|
binanceClient.SpotApi.CommonSpotClient,
|
||||||
bitfinexClient.SpotApi.CommonSpotClient,
|
bitfinexClient.SpotApi.CommonSpotClient,
|
||||||
bittrexClient.SpotApi.CommonSpotClient,
|
bitgetClient.SpotApi.CommonSpotClient,
|
||||||
|
bittrexClient.SpotApi.CommonSpotClient,
|
||||||
bybitClient.SpotApiV1.CommonSpotClient,
|
bybitClient.SpotApiV1.CommonSpotClient,
|
||||||
coinexClient.SpotApi.CommonSpotClient,
|
coinexClient.SpotApi.CommonSpotClient,
|
||||||
huobiClient.SpotApi.CommonSpotClient,
|
huobiClient.SpotApi.CommonSpotClient,
|
||||||
krakenClient.SpotApi.CommonSpotClient,
|
krakenClient.SpotApi.CommonSpotClient,
|
||||||
kucoinClient.SpotApi.CommonSpotClient
|
kucoinClient.SpotApi.CommonSpotClient,
|
||||||
|
okxClient.UnifiedApi.CommonSpotClient
|
||||||
};
|
};
|
||||||
|
|
||||||
var tasks = clients.Select(c => (c.ExchangeName, c.GetTickerAsync(c.GetSymbolName("ETH", "BTC"))));
|
var tasks = clients.Select(c => (c.ExchangeName, c.GetTickerAsync(c.GetSymbolName("ETH", "BTC"))));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Binance.Net;
|
|||||||
using Binance.Net.Clients;
|
using Binance.Net.Clients;
|
||||||
using Binance.Net.Interfaces.Clients;
|
using Binance.Net.Interfaces.Clients;
|
||||||
using Bitfinex.Net;
|
using Bitfinex.Net;
|
||||||
|
using Bitget.Net;
|
||||||
using Bittrex.Net;
|
using Bittrex.Net;
|
||||||
using Bybit.Net;
|
using Bybit.Net;
|
||||||
using CoinEx.Net;
|
using CoinEx.Net;
|
||||||
@@ -16,6 +17,7 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using OKX.Net;
|
||||||
|
|
||||||
namespace BlazorClient
|
namespace BlazorClient
|
||||||
{
|
{
|
||||||
@@ -48,12 +50,14 @@ namespace BlazorClient
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddBitfinex();
|
services.AddBitfinex();
|
||||||
|
services.AddBitget();
|
||||||
services.AddBittrex();
|
services.AddBittrex();
|
||||||
services.AddBybit();
|
services.AddBybit();
|
||||||
services.AddCoinEx();
|
services.AddCoinEx();
|
||||||
services.AddHuobi();
|
services.AddHuobi();
|
||||||
services.AddKraken();
|
services.AddKraken();
|
||||||
services.AddKucoin();
|
services.AddKucoin();
|
||||||
|
services.AddOKX();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
@using BlazorClient.Shared
|
@using BlazorClient.Shared
|
||||||
@using Binance.Net.Interfaces.Clients;
|
@using Binance.Net.Interfaces.Clients;
|
||||||
@using Bitfinex.Net.Interfaces.Clients;
|
@using Bitfinex.Net.Interfaces.Clients;
|
||||||
|
@using Bitget.Net.Interfaces.Clients;
|
||||||
@using Bittrex.Net.Interfaces.Clients;
|
@using Bittrex.Net.Interfaces.Clients;
|
||||||
@using Bybit.Net.Interfaces.Clients;
|
@using Bybit.Net.Interfaces.Clients;
|
||||||
@using CoinEx.Net.Interfaces.Clients;
|
@using CoinEx.Net.Interfaces.Clients;
|
||||||
@using Huobi.Net.Interfaces.Clients;
|
@using Huobi.Net.Interfaces.Clients;
|
||||||
@using Kraken.Net.Interfaces.Clients;
|
@using Kraken.Net.Interfaces.Clients;
|
||||||
@using Kucoin.Net.Interfaces.Clients;
|
@using Kucoin.Net.Interfaces.Clients;
|
||||||
|
@using OKX.Net.Interfaces.Clients;
|
||||||
@@ -6,14 +6,16 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="9.0.1" />
|
<PackageReference Include="Binance.Net" Version="9.1.5" />
|
||||||
<PackageReference Include="Bitfinex.Net" Version="6.0.0" />
|
<PackageReference Include="Bitfinex.Net" Version="7.0.4" />
|
||||||
<PackageReference Include="Bittrex.Net" Version="8.0.0" />
|
<PackageReference Include="Bittrex.Net" Version="8.0.3" />
|
||||||
<PackageReference Include="Bybit.Net" Version="3.0.0" />
|
<PackageReference Include="Bybit.Net" Version="3.2.1" />
|
||||||
<PackageReference Include="CoinEx.Net" Version="6.0.0" />
|
<PackageReference Include="CoinEx.Net" Version="6.0.3" />
|
||||||
<PackageReference Include="Huobi.Net" Version="5.0.0" />
|
<PackageReference Include="Huobi.Net" Version="5.0.3" />
|
||||||
<PackageReference Include="KrakenExchange.Net" Version="4.0.0" />
|
<PackageReference Include="JK.Bitget.Net" Version="1.0.0" />
|
||||||
<PackageReference Include="Kucoin.Net" Version="5.0.0" />
|
<PackageReference Include="JK.OKX.Net" Version="1.4.2" />
|
||||||
|
<PackageReference Include="KrakenExchange.Net" Version="4.1.5" />
|
||||||
|
<PackageReference Include="Kucoin.Net" Version="5.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -31,6 +31,30 @@ 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 6.2.3 - 02 Dec 2023
|
||||||
|
* Fixed requestBodyFormat parameter handling
|
||||||
|
|
||||||
|
* Version 6.2.2 - 02 Dec 2023
|
||||||
|
* Added support for specifying the request body content type on a per request basis
|
||||||
|
* Added DecimalStringWriterConverter
|
||||||
|
* Added RequestId to WebCallResult model
|
||||||
|
* Updated response logging
|
||||||
|
|
||||||
|
* Version 6.2.1 - 28 Oct 2023
|
||||||
|
* Utility methods
|
||||||
|
|
||||||
|
* Version 6.2.0 - 24 Oct 2023
|
||||||
|
* Added SerializerOptions helper class for setting a default serializer
|
||||||
|
* Added ParameterCollection helper class for easier parameter definition
|
||||||
|
* Added extra helper methods AuthenticationProvider
|
||||||
|
* Remove interface entries meant for internal use
|
||||||
|
* Added support for writing int values to the EnumConverter
|
||||||
|
|
||||||
|
* Version 6.1.5 - 08 Oct 2023
|
||||||
|
* Added UpdateType to socket DataEvent
|
||||||
|
* Added additional scenarios for BoolConverter
|
||||||
|
* Updated some logging
|
||||||
|
|
||||||
* Version 6.1.4 - 23 Sep 2023
|
* Version 6.1.4 - 23 Sep 2023
|
||||||
* Added BoolConverter
|
* Added BoolConverter
|
||||||
* Added parameter for logging warning message on missing enum entry to EnumConverter
|
* Added parameter for logging warning message on missing enum entry to EnumConverter
|
||||||
|
|||||||
+4
-4
@@ -82,7 +82,7 @@ All clients have access to the following options, specific implementations might
|
|||||||
|
|
||||||
|Option|Description|Default|
|
|Option|Description|Default|
|
||||||
|------|-----------|-------|
|
|------|-----------|-------|
|
||||||
|`AutoTimestamp`|Whether or not the library should attempt to sync the time between the client and server. If the time between server and client is not in sync authentication errors might occur. This option should be disabled when the client time sure to be in sync.|`true`|
|
|`AutoTimestamp`|Whether or not the library should attempt to sync the time between the client and server. If the time between server and client is not in sync authentication errors might occur. This option should be disabled when the client time sure is to be in sync.|`true`|
|
||||||
|`TimestampRecalculationInterval`|The interval of how often the time synchronization between client and server should be executed| `TimeSpan.FromHours(1)`
|
|`TimestampRecalculationInterval`|The interval of how often the time synchronization between client and server should be executed| `TimeSpan.FromHours(1)`
|
||||||
|`Environment`|The environment the library should talk to. Some exchanges have testnet/sandbox environments which can be used instead of the real exchange. The environment option can be used to switch between different trade environments|`Live environment`
|
|`Environment`|The environment the library should talk to. Some exchanges have testnet/sandbox environments which can be used instead of the real exchange. The environment option can be used to switch between different trade environments|`Live environment`
|
||||||
|
|
||||||
@@ -104,8 +104,8 @@ All clients have access to the following options, specific implementations might
|
|||||||
|
|
||||||
|Option|Description|Default|
|
|Option|Description|Default|
|
||||||
|------|-----------|-------|
|
|------|-----------|-------|
|
||||||
|`ApiCredentials`|If set to `true` the originally received Json data will be output as well as the deserialized object. For `RestClient` calls the data will be in the `WebCallResult<T>.OriginalData` property, for `SocketClient` subscriptions the data will be available in the `DataEvent<T>.OriginalData` property when receiving an update. Overrides the Base client options `OutputOriginalData` option if set| `false`
|
|`ApiCredentials`|The API credentials to use for accessing protected endpoints. Can either be an API key/secret using Hmac encryption or an API key/private key using RSA encryption for exchanges that support that. See [Credentials](#credentials). Setting ApiCredentials on the Api Options will override any default ApiCredentials on the `Base client options`| `null`
|
||||||
|`OutputOriginalData`|The base address to the API. All calls to the API will use this base address as basis for the endpoints. This allows for swapping to test API's or swapping to a different cluster for example. Available base addresses are defined in the [Library]ApiAddresses helper class, for example `KucoinApiAddresses`|Depends on implementation
|
|`OutputOriginalData`|If set to `true` the originally received Json data will be output as well as the deserialized object. For `RestClient` calls the data will be in the `WebCallResult<T>.OriginalData` property, for `SocketClient` subscriptions the data will be available in the `DataEvent<T>.OriginalData` property when receiving an update.|False
|
||||||
|
|
||||||
**Options for Rest Api Client (extension of base api client options)**
|
**Options for Rest Api Client (extension of base api client options)**
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ All clients have access to the following options, specific implementations might
|
|||||||
|------|-----------|-------|
|
|------|-----------|-------|
|
||||||
|`RateLimiters`|A list of `IRateLimiter`s to use.|`new List<IRateLimiter>()`|
|
|`RateLimiters`|A list of `IRateLimiter`s to use.|`new List<IRateLimiter>()`|
|
||||||
|`RateLimitingBehaviour`|What should happen when a rate limit is reached.|`RateLimitingBehaviour.Wait`|
|
|`RateLimitingBehaviour`|What should happen when a rate limit is reached.|`RateLimitingBehaviour.Wait`|
|
||||||
|`AutoTimestamp`|Whether or not the library should attempt to sync the time between the client and server. If the time between server and client is not in sync authentication errors might occur. This option should be disabled when the client time sure to be in sync. Overrides the Rest client options `AutoTimestamp` option if set|`null`|
|
|`AutoTimestamp`|Whether or not the library should attempt to sync the time between the client and server. If the time between server and client is not in sync authentication errors might occur. This option should be disabled when the client time is sure to be in sync. Overrides the Rest client options `AutoTimestamp` option if set|`null`|
|
||||||
|`TimestampRecalculationInterval`|The interval of how often the time synchronization between client and server should be executed. Overrides the Rest client options `TimestampRecalculationInterval` option if set| `TimeSpan.FromHours(1)`
|
|`TimestampRecalculationInterval`|The interval of how often the time synchronization between client and server should be executed. Overrides the Rest client options `TimestampRecalculationInterval` option if set| `TimeSpan.FromHours(1)`
|
||||||
|
|
||||||
**Options for Socket Api Client (extension of base api client options)**
|
**Options for Socket Api Client (extension of base api client options)**
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ These will always be on the latest CryptoExchange.Net version and the latest ver
|
|||||||
|-|-|-|
|
|-|-|-|
|
||||||
|<a href="https://github.com/JKorf/Binance.Net"><img src="https://github.com/JKorf/Binance.Net/blob/master/Binance.Net/Icon/icon.png?raw=true"></a>|Binance|https://jkorf.github.io/Binance.Net/|
|
|<a href="https://github.com/JKorf/Binance.Net"><img src="https://github.com/JKorf/Binance.Net/blob/master/Binance.Net/Icon/icon.png?raw=true"></a>|Binance|https://jkorf.github.io/Binance.Net/|
|
||||||
|<a href="https://github.com/JKorf/Bitfinex.Net"><img src="https://github.com/JKorf/Bitfinex.Net/blob/master/Bitfinex.Net/Icon/icon.png?raw=true"></a>|Bitfinex|https://jkorf.github.io/Bitfinex.Net/|
|
|<a href="https://github.com/JKorf/Bitfinex.Net"><img src="https://github.com/JKorf/Bitfinex.Net/blob/master/Bitfinex.Net/Icon/icon.png?raw=true"></a>|Bitfinex|https://jkorf.github.io/Bitfinex.Net/|
|
||||||
|
|<a href="https://github.com/JKorf/Bitget.Net"><img src="https://github.com/JKorf/Bitget.Net/blob/master/Bitget.Net/Icon/icon.png?raw=true"></a>|Bitget|https://jkorf.github.io/Bitget.Net/|
|
||||||
|<a href="https://github.com/JKorf/Bittrex.Net"><img src="https://github.com/JKorf/Bittrex.Net/blob/master/Bittrex.Net/Icon/icon.png?raw=true"></a>|Bittrex|https://jkorf.github.io/Bittrex.Net/|
|
|<a href="https://github.com/JKorf/Bittrex.Net"><img src="https://github.com/JKorf/Bittrex.Net/blob/master/Bittrex.Net/Icon/icon.png?raw=true"></a>|Bittrex|https://jkorf.github.io/Bittrex.Net/|
|
||||||
|<a href="https://github.com/JKorf/Bybit.Net"><img src="https://github.com/JKorf/Bybit.Net/blob/main/ByBit.Net/Icon/icon.png?raw=true"></a>|Bybit|https://jkorf.github.io/Bybit.Net/|
|
|<a href="https://github.com/JKorf/Bybit.Net"><img src="https://github.com/JKorf/Bybit.Net/blob/main/ByBit.Net/Icon/icon.png?raw=true"></a>|Bybit|https://jkorf.github.io/Bybit.Net/|
|
||||||
|<a href="https://github.com/JKorf/CoinEx.Net"><img src="https://github.com/JKorf/CoinEx.Net/blob/master/CoinEx.Net/Icon/icon.png?raw=true"></a>|CoinEx|https://jkorf.github.io/CoinEx.Net/|
|
|<a href="https://github.com/JKorf/CoinEx.Net"><img src="https://github.com/JKorf/CoinEx.Net/blob/master/CoinEx.Net/Icon/icon.png?raw=true"></a>|CoinEx|https://jkorf.github.io/CoinEx.Net/|
|
||||||
|
|||||||
Reference in New Issue
Block a user