mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-11-03 20:07:33 +00:00
Compare commits
No commits in common. "master" and "CryptoExchange.Net.Protobuf.9.8.0" have entirely different histories.
master
...
CryptoExch
@ -6,9 +6,9 @@
|
|||||||
<PackageId>CryptoExchange.Net.Protobuf</PackageId>
|
<PackageId>CryptoExchange.Net.Protobuf</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>Protobuf support for CryptoExchange.Net</Description>
|
<Description>Protobuf support for CryptoExchange.Net</Description>
|
||||||
<PackageVersion>9.12.0</PackageVersion>
|
<PackageVersion>9.8.0</PackageVersion>
|
||||||
<AssemblyVersion>9.12.0</AssemblyVersion>
|
<AssemblyVersion>9.8.0</AssemblyVersion>
|
||||||
<FileVersion>9.12.0</FileVersion>
|
<FileVersion>9.8.0</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
|
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
@ -41,7 +41,7 @@
|
|||||||
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
|
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CryptoExchange.Net" Version="9.12.0" />
|
<PackageReference Include="CryptoExchange.Net" Version="9.8.0" />
|
||||||
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
<PackageReference Include="protobuf-net" Version="3.2.56" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -5,21 +5,6 @@
|
|||||||
Protobuf support for CryptoExchange.Net.
|
Protobuf support for CryptoExchange.Net.
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
* Version 9.12.0 - 03 Nov 2025
|
|
||||||
* Updated CryptoExchange.Net version to 9.12.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
|
||||||
|
|
||||||
* Version 9.11.1 - 30 Oct 2025
|
|
||||||
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
|
||||||
|
|
||||||
* Version 9.11.0 - 30 Oct 2025
|
|
||||||
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
|
||||||
|
|
||||||
* Version 9.10.0 - 15 Oct 2025
|
|
||||||
* Updated CryptoExchange.Net version to 9.10.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
|
||||||
|
|
||||||
* Version 9.9.0 - 06 Oct 2025
|
|
||||||
* Updated CryptoExchange.Net version to 9.9.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
|
||||||
|
|
||||||
* Version 9.8.0 - 30 Sep 2025
|
* Version 9.8.0 - 30 Sep 2025
|
||||||
* Updated CryptoExchange.Net version to 9.8.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
* Updated CryptoExchange.Net version to 9.8.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"></PackageReference>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"></PackageReference>
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="NUnit" Version="4.4.0"></PackageReference>
|
<PackageReference Include="NUnit" Version="4.3.2"></PackageReference>
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0"></PackageReference>
|
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"></PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -202,7 +202,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
await sub;
|
await sub;
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed);
|
ClassicAssert.IsFalse(client.SubClient.TestSubscription.Confirmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase()]
|
[TestCase()]
|
||||||
@ -225,7 +225,7 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
await sub;
|
await sub;
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed);
|
Assert.That(client.SubClient.TestSubscription.Confirmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -102,6 +102,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
if (ClientOptions == null)
|
if (ClientOptions == null)
|
||||||
throw new InvalidOperationException("Client should have called Initialize before adding API clients");
|
throw new InvalidOperationException("Client should have called Initialize before adding API clients");
|
||||||
|
|
||||||
|
_logger.Log(LogLevel.Trace, $" {apiClient.GetType().Name}, base address: {apiClient.BaseAddress}");
|
||||||
ApiClients.Add(apiClient);
|
ApiClients.Add(apiClient);
|
||||||
return apiClient;
|
return apiClient;
|
||||||
}
|
}
|
||||||
@ -121,6 +122,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
|
_logger.Log(LogLevel.Debug, "Disposing client");
|
||||||
foreach (var client in ApiClients)
|
foreach (var client in ApiClients)
|
||||||
client.Dispose();
|
client.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,8 +21,6 @@ namespace CryptoExchange.Net.Clients
|
|||||||
protected BaseRestClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
|
protected BaseRestClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
|
_logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
|
||||||
|
|
||||||
LibraryHelpers.StaticLogger = loggerFactory?.CreateLogger(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,8 +40,6 @@ namespace CryptoExchange.Net.Clients
|
|||||||
protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
|
protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
|
_logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
|
||||||
|
|
||||||
LibraryHelpers.StaticLogger = loggerFactory?.CreateLogger(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -269,7 +269,6 @@ namespace CryptoExchange.Net.Clients
|
|||||||
return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
|
return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Subscribing;
|
|
||||||
var waitEvent = new AsyncResetEvent(false);
|
var waitEvent = new AsyncResetEvent(false);
|
||||||
var subQuery = subscription.CreateSubscriptionQuery(socketConnection);
|
var subQuery = subscription.CreateSubscriptionQuery(socketConnection);
|
||||||
if (subQuery != null)
|
if (subQuery != null)
|
||||||
@ -280,7 +279,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
{
|
{
|
||||||
waitEvent?.Set();
|
waitEvent?.Set();
|
||||||
var isTimeout = subResult.Error is CancellationRequestedError;
|
var isTimeout = subResult.Error is CancellationRequestedError;
|
||||||
if (isTimeout && subscription.Status == SubscriptionStatus.Subscribed)
|
if (isTimeout && subscription.Confirmed)
|
||||||
{
|
{
|
||||||
// No response received, but the subscription did receive updates. We'll assume success
|
// No response received, but the subscription did receive updates. We'll assume success
|
||||||
}
|
}
|
||||||
@ -288,7 +287,6 @@ namespace CryptoExchange.Net.Clients
|
|||||||
{
|
{
|
||||||
_logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString());
|
_logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString());
|
||||||
// If this was a timeout we still need to send an unsubscribe to prevent messages coming in later
|
// If this was a timeout we still need to send an unsubscribe to prevent messages coming in later
|
||||||
subscription.Status = SubscriptionStatus.Pending;
|
|
||||||
await socketConnection.CloseAsync(subscription).ConfigureAwait(false);
|
await socketConnection.CloseAsync(subscription).ConfigureAwait(false);
|
||||||
return new CallResult<UpdateSubscription>(subResult.Error!);
|
return new CallResult<UpdateSubscription>(subResult.Error!);
|
||||||
}
|
}
|
||||||
@ -297,7 +295,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
subscription.HandleSubQueryResponse(subQuery.Response!);
|
subscription.HandleSubQueryResponse(subQuery.Response!);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Subscribed;
|
subscription.Confirmed = true;
|
||||||
if (ct != default)
|
if (ct != default)
|
||||||
{
|
{
|
||||||
subscription.CancellationTokenRegistration = ct.Register(async () =>
|
subscription.CancellationTokenRegistration = ct.Register(async () =>
|
||||||
@ -849,7 +847,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
cs.SubscriptionStates.ForEach(subState =>
|
cs.SubscriptionStates.ForEach(subState =>
|
||||||
{
|
{
|
||||||
sb.AppendLine($"\t\t\tId: {subState.Id}");
|
sb.AppendLine($"\t\t\tId: {subState.Id}");
|
||||||
sb.AppendLine($"\t\t\tStatus: {subState.Status}");
|
sb.AppendLine($"\t\t\tConfirmed: {subState.Confirmed}");
|
||||||
sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}");
|
sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}");
|
||||||
sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]");
|
sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -48,7 +47,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
if (typeToConvert == typeof(bool))
|
if (typeToConvert == typeof(bool))
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("Received null bool value, but property type is not a nullable bool");
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null bool value, but property type is not a nullable bool");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -15,8 +14,8 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
{
|
{
|
||||||
private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||||
private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000;
|
private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000;
|
||||||
private const decimal _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000m;
|
private const double _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000d;
|
||||||
private const decimal _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000m / 1000;
|
private const double _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000d / 1000;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type typeToConvert)
|
public override bool CanConvert(Type typeToConvert)
|
||||||
@ -40,17 +39,17 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
if (reader.TokenType == JsonTokenType.Null)
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
{
|
{
|
||||||
if (typeToConvert == typeof(DateTime))
|
if (typeToConvert == typeof(DateTime))
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("DateTime value of null, but property is not nullable");
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | DateTime value of null, but property is not nullable");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.TokenType is JsonTokenType.Number)
|
if (reader.TokenType is JsonTokenType.Number)
|
||||||
{
|
{
|
||||||
var decValue = reader.GetDecimal();
|
var longValue = reader.GetDouble();
|
||||||
if (decValue == 0 || decValue < 0)
|
if (longValue == 0 || longValue < 0)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
return ParseFromDecimal(decValue);
|
return ParseFromDouble(longValue);
|
||||||
}
|
}
|
||||||
else if (reader.TokenType is JsonTokenType.String)
|
else if (reader.TokenType is JsonTokenType.String)
|
||||||
{
|
{
|
||||||
@ -58,7 +57,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
if (string.IsNullOrWhiteSpace(stringValue)
|
if (string.IsNullOrWhiteSpace(stringValue)
|
||||||
|| stringValue == "-1"
|
|| stringValue == "-1"
|
||||||
|| stringValue == "0001-01-01T00:00:00Z"
|
|| stringValue == "0001-01-01T00:00:00Z"
|
||||||
|| decimal.TryParse(stringValue, out var decVal) && decVal == 0)
|
|| double.TryParse(stringValue, out var doubleVal) && doubleVal == 0)
|
||||||
{
|
{
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
@ -89,24 +88,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse a double value to datetime
|
/// Parse a long value to datetime
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DateTime ParseFromDouble(double value)
|
/// <param name="longValue"></param>
|
||||||
=> ParseFromDecimal((decimal)value);
|
/// <returns></returns>
|
||||||
|
public static DateTime ParseFromDouble(double longValue)
|
||||||
/// <summary>
|
|
||||||
/// Parse a decimal value to datetime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ParseFromDecimal(decimal value)
|
|
||||||
{
|
{
|
||||||
if (value < 19999999999)
|
if (longValue < 19999999999)
|
||||||
return ConvertFromSeconds(value);
|
return ConvertFromSeconds(longValue);
|
||||||
if (value < 19999999999999)
|
if (longValue < 19999999999999)
|
||||||
return ConvertFromMilliseconds(value);
|
return ConvertFromMilliseconds(longValue);
|
||||||
if (value < 19999999999999999)
|
if (longValue < 19999999999999999)
|
||||||
return ConvertFromMicroseconds(value);
|
return ConvertFromMicroseconds(longValue);
|
||||||
|
|
||||||
return ConvertFromNanoseconds(value);
|
return ConvertFromNanoseconds(longValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -125,7 +120,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|| !int.TryParse(stringValue.Substring(8, 2), out var hour)
|
|| !int.TryParse(stringValue.Substring(8, 2), out var hour)
|
||||||
|| !int.TryParse(stringValue.Substring(10, 2), out var minute))
|
|| !int.TryParse(stringValue.Substring(10, 2), out var minute))
|
||||||
{
|
{
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
|
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
|
||||||
@ -138,7 +133,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|| !int.TryParse(stringValue.Substring(4, 2), out var month)
|
|| !int.TryParse(stringValue.Substring(4, 2), out var month)
|
||||||
|| !int.TryParse(stringValue.Substring(6, 2), out var day))
|
|| !int.TryParse(stringValue.Substring(6, 2), out var day))
|
||||||
{
|
{
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
|
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
|
||||||
@ -151,25 +146,25 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|| !int.TryParse(stringValue.Substring(2, 2), out var month)
|
|| !int.TryParse(stringValue.Substring(2, 2), out var month)
|
||||||
|| !int.TryParse(stringValue.Substring(4, 2), out var day))
|
|| !int.TryParse(stringValue.Substring(4, 2), out var day))
|
||||||
{
|
{
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
|
Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc);
|
return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decimal.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var decimalValue))
|
if (double.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue))
|
||||||
{
|
{
|
||||||
// Parse 1637745563.000 format
|
// Parse 1637745563.000 format
|
||||||
if (decimalValue <= 0)
|
if (doubleValue <= 0)
|
||||||
return default;
|
return default;
|
||||||
if (decimalValue < 19999999999)
|
if (doubleValue < 19999999999)
|
||||||
return ConvertFromSeconds(decimalValue);
|
return ConvertFromSeconds(doubleValue);
|
||||||
if (decimalValue < 19999999999999)
|
if (doubleValue < 19999999999999)
|
||||||
return ConvertFromMilliseconds(decimalValue);
|
return ConvertFromMilliseconds((long)doubleValue);
|
||||||
if (decimalValue < 19999999999999999)
|
if (doubleValue < 19999999999999999)
|
||||||
return ConvertFromMicroseconds(decimalValue);
|
return ConvertFromMicroseconds((long)doubleValue);
|
||||||
|
|
||||||
return ConvertFromNanoseconds(decimalValue);
|
return ConvertFromNanoseconds((long)doubleValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stringValue.Length == 10)
|
if (stringValue.Length == 10)
|
||||||
@ -180,7 +175,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
|| !int.TryParse(values[1], out var month)
|
|| !int.TryParse(values[1], out var month)
|
||||||
|| !int.TryParse(values[2], out var day))
|
|| !int.TryParse(values[2], out var day))
|
||||||
{
|
{
|
||||||
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
|
Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,70 +188,54 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a seconds since epoch (01-01-1970) value to DateTime
|
/// Convert a seconds since epoch (01-01-1970) value to DateTime
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DateTime ConvertFromSeconds(decimal seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond));
|
/// <param name="seconds"></param>
|
||||||
/// <summary>
|
/// <returns></returns>
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
public static DateTime ConvertFromSeconds(double seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond));
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromSeconds(double seconds) => ConvertFromSeconds((decimal)seconds);
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromSeconds(long seconds) => ConvertFromSeconds((decimal)seconds);
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a milliseconds since epoch (01-01-1970) value to DateTime
|
/// Convert a milliseconds since epoch (01-01-1970) value to DateTime
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DateTime ConvertFromMilliseconds(decimal milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond));
|
/// <param name="milliseconds"></param>
|
||||||
/// <summary>
|
/// <returns></returns>
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond));
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromMilliseconds(double milliseconds) => ConvertFromMilliseconds((decimal)milliseconds);
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromMilliseconds(long milliseconds) => ConvertFromMilliseconds((decimal)milliseconds);
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a microseconds since epoch (01-01-1970) value to DateTime
|
/// Convert a microseconds since epoch (01-01-1970) value to DateTime
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DateTime ConvertFromMicroseconds(decimal microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * _ticksPerMicrosecond));
|
/// <param name="microseconds"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DateTime ConvertFromMicroseconds(double microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * _ticksPerMicrosecond));
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DateTime ConvertFromMicroseconds(double microseconds) => ConvertFromMicroseconds((decimal)microseconds);
|
/// <param name="nanoseconds"></param>
|
||||||
/// <summary>
|
/// <returns></returns>
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
public static DateTime ConvertFromNanoseconds(double nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * _ticksPerNanosecond));
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromMicroseconds(long microseconds) => ConvertFromMicroseconds((decimal)microseconds);
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromNanoseconds(decimal nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * _ticksPerNanosecond));
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromNanoseconds(double nanoseconds) => ConvertFromNanoseconds((decimal)nanoseconds);
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
|
|
||||||
/// </summary>
|
|
||||||
public static DateTime ConvertFromNanoseconds(long nanoseconds) => ConvertFromNanoseconds((decimal)nanoseconds);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a DateTime value to seconds since epoch (01-01-1970) value
|
/// Convert a DateTime value to seconds since epoch (01-01-1970) value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <returns></returns>
|
||||||
[return: NotNullIfNotNull("time")]
|
[return: NotNullIfNotNull("time")]
|
||||||
public static long? ConvertToSeconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalSeconds);
|
public static long? ConvertToSeconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalSeconds);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a DateTime value to milliseconds since epoch (01-01-1970) value
|
/// Convert a DateTime value to milliseconds since epoch (01-01-1970) value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <returns></returns>
|
||||||
[return: NotNullIfNotNull("time")]
|
[return: NotNullIfNotNull("time")]
|
||||||
public static long? ConvertToMilliseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalMilliseconds);
|
public static long? ConvertToMilliseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalMilliseconds);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a DateTime value to microseconds since epoch (01-01-1970) value
|
/// Convert a DateTime value to microseconds since epoch (01-01-1970) value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <returns></returns>
|
||||||
[return: NotNullIfNotNull("time")]
|
[return: NotNullIfNotNull("time")]
|
||||||
public static long? ConvertToMicroseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerMicrosecond);
|
public static long? ConvertToMicroseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerMicrosecond);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a DateTime value to nanoseconds since epoch (01-01-1970) value
|
/// Convert a DateTime value to nanoseconds since epoch (01-01-1970) value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <returns></returns>
|
||||||
[return: NotNullIfNotNull("time")]
|
[return: NotNullIfNotNull("time")]
|
||||||
public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond);
|
public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using CryptoExchange.Net.Attributes;
|
using CryptoExchange.Net.Attributes;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -81,7 +79,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
}
|
}
|
||||||
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
|
return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
|
||||||
@ -100,13 +98,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
|
var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn);
|
||||||
if (t == null)
|
if (t == null)
|
||||||
{
|
{
|
||||||
if (isEmptyString && !_unknownValuesWarned.Contains(null))
|
if (warn)
|
||||||
{
|
{
|
||||||
// We received an empty string and have no mapping for it, and the property isn't nullable
|
if (isEmptyString)
|
||||||
LibraryHelpers.StaticLogger?.LogWarning($"Received null or empty enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
|
{
|
||||||
|
// We received an empty string and have no mapping for it, and the property isn't nullable
|
||||||
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received empty string as enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new T(); // return default value
|
return new T(); // return default value
|
||||||
@ -117,9 +122,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString)
|
private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn)
|
||||||
{
|
{
|
||||||
isEmptyString = false;
|
isEmptyString = false;
|
||||||
|
warn = false;
|
||||||
var enumType = typeof(T);
|
var enumType = typeof(T);
|
||||||
if (_mapping == null)
|
if (_mapping == null)
|
||||||
_mapping = AddMapping();
|
_mapping = AddMapping();
|
||||||
@ -148,8 +154,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
// We received an enum value but weren't able to parse it.
|
// We received an enum value but weren't able to parse it.
|
||||||
if (!_unknownValuesWarned.Contains(stringValue))
|
if (!_unknownValuesWarned.Contains(stringValue))
|
||||||
{
|
{
|
||||||
|
warn = true;
|
||||||
_unknownValuesWarned.Add(stringValue!);
|
_unknownValuesWarned.Add(stringValue!);
|
||||||
LibraryHelpers.StaticLogger?.LogWarning($"Cannot map enum value. EnumType: {enumType.Name}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo");
|
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Cannot map enum value. EnumType: {enumType.Name}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
||||||
<PackageVersion>9.12.0</PackageVersion>
|
<PackageVersion>9.8.0</PackageVersion>
|
||||||
<AssemblyVersion>9.12.0</AssemblyVersion>
|
<AssemblyVersion>9.8.0</AssemblyVersion>
|
||||||
<FileVersion>9.12.0</FileVersion>
|
<FileVersion>9.8.0</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
@ -51,11 +51,11 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||||
<PackageReference Include="System.Text.Json" Version="9.0.10" />
|
<PackageReference Include="System.Text.Json" Version="9.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Client Packages">
|
<ItemGroup Label="Transitive Client Packages">
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@ -416,36 +416,6 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsDelivery(this TradingMode type) => type == TradingMode.DeliveryInverse || type == TradingMode.DeliveryLinear;
|
public static bool IsDelivery(this TradingMode type) => type == TradingMode.DeliveryInverse || type == TradingMode.DeliveryLinear;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the account type is a futures account
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsFuturesAccount(this SharedAccountType type) =>
|
|
||||||
type == SharedAccountType.PerpetualLinearFutures
|
|
||||||
|| type == SharedAccountType.DeliveryLinearFutures
|
|
||||||
|| type == SharedAccountType.PerpetualInverseFutures
|
|
||||||
|| type == SharedAccountType.DeliveryInverseFutures;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the account type is a margin account
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsMarginAccount(this SharedAccountType type) =>
|
|
||||||
type == SharedAccountType.CrossMargin
|
|
||||||
|| type == SharedAccountType.IsolatedMargin;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Map a TradingMode value to a SharedAccountType enum value
|
|
||||||
/// </summary>
|
|
||||||
public static SharedAccountType ToAccountType(this TradingMode mode)
|
|
||||||
{
|
|
||||||
if (mode == TradingMode.Spot) return SharedAccountType.Spot;
|
|
||||||
if (mode == TradingMode.PerpetualLinear) return SharedAccountType.PerpetualLinearFutures;
|
|
||||||
if (mode == TradingMode.PerpetualInverse) return SharedAccountType.PerpetualInverseFutures;
|
|
||||||
if (mode == TradingMode.DeliveryInverse) return SharedAccountType.DeliveryInverseFutures;
|
|
||||||
if (mode == TradingMode.DeliveryLinear) return SharedAccountType.DeliveryLinearFutures;
|
|
||||||
|
|
||||||
throw new ArgumentException(nameof(mode), "Unmapped trading mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register rest client interfaces
|
/// Register rest client interfaces
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -475,8 +445,6 @@ namespace CryptoExchange.Net
|
|||||||
services.AddTransient(x => (IFeeRestClient)client(x)!);
|
services.AddTransient(x => (IFeeRestClient)client(x)!);
|
||||||
if (typeof(IBookTickerRestClient).IsAssignableFrom(typeof(T)))
|
if (typeof(IBookTickerRestClient).IsAssignableFrom(typeof(T)))
|
||||||
services.AddTransient(x => (IBookTickerRestClient)client(x)!);
|
services.AddTransient(x => (IBookTickerRestClient)client(x)!);
|
||||||
if (typeof(ITransferRestClient).IsAssignableFrom(typeof(T)))
|
|
||||||
services.AddTransient(x => (ITransferRestClient)client(x)!);
|
|
||||||
|
|
||||||
if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T)))
|
if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T)))
|
||||||
services.AddTransient(x => (ISpotOrderRestClient)client(x)!);
|
services.AddTransient(x => (ISpotOrderRestClient)client(x)!);
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@ -13,50 +12,11 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class LibraryHelpers
|
public static class LibraryHelpers
|
||||||
{
|
{
|
||||||
private static ILogger? _staticLogger;
|
|
||||||
/// <summary>
|
|
||||||
/// Static logger
|
|
||||||
/// </summary>
|
|
||||||
public static ILogger? StaticLogger
|
|
||||||
{
|
|
||||||
get => _staticLogger;
|
|
||||||
internal set
|
|
||||||
{
|
|
||||||
if (_staticLogger != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_staticLogger = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Client order id separator
|
/// Client order id separator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ClientOrderIdSeparator = "JK";
|
public const string ClientOrderIdSeparator = "JK";
|
||||||
|
|
||||||
private static Dictionary<string, string> _defaultClientReferences = new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "Binance.Spot", "x-VICEW9VV" },
|
|
||||||
{ "Binance.Futures", "x-d63tKbx3" },
|
|
||||||
{ "BingX", "easytrading" },
|
|
||||||
{ "Bitfinex", "kCCe-CNBO" },
|
|
||||||
{ "Bitget", "6x21p" },
|
|
||||||
{ "BitMart", "EASYTRADING0001" },
|
|
||||||
{ "BitMEX", "Sent from JKorf" },
|
|
||||||
{ "BloFin", "5c07cf695885c282" },
|
|
||||||
{ "Bybit", "Zx000356" },
|
|
||||||
{ "CoinEx", "x-147866029-" },
|
|
||||||
{ "GateIo", "copytraderpw" },
|
|
||||||
{ "HTX", "AA1ef14811" },
|
|
||||||
{ "Kucoin.FuturesName", "Easytradingfutures" },
|
|
||||||
{ "Kucoin.FuturesKey", "9e08c05f-454d-4580-82af-2f4c7027fd00" },
|
|
||||||
{ "Kucoin.SpotName", "Easytrading" },
|
|
||||||
{ "Kucoin.SpotKey", "f8ae62cb-2b3d-420c-8c98-e1c17dd4e30a" },
|
|
||||||
{ "Mexc", "EASYT" },
|
|
||||||
{ "OKX", "1425d83a94fbBCDE" },
|
|
||||||
{ "XT", "4XWeqN10M1fcoI5L" },
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Apply broker id to a client order id
|
/// Apply broker id to a client order id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -87,22 +47,6 @@ namespace CryptoExchange.Net
|
|||||||
return clientOrderId;
|
return clientOrderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the client reference for an exchange if available
|
|
||||||
/// </summary>
|
|
||||||
public static string GetClientReference(Func<string?> optionsReference, string exchange, string? topic = null)
|
|
||||||
{
|
|
||||||
var optionsValue = optionsReference();
|
|
||||||
if (!string.IsNullOrEmpty(optionsValue))
|
|
||||||
return optionsValue!;
|
|
||||||
|
|
||||||
var key = exchange;
|
|
||||||
if (topic != null)
|
|
||||||
key += "." + topic;
|
|
||||||
|
|
||||||
return _defaultClientReferences.TryGetValue(key, out var id) ? id : throw new KeyNotFoundException($"{exchange} not found in configuration");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new HttpMessageHandler instance
|
/// Create a new HttpMessageHandler instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
private static readonly Action<ILogger, int, Exception?> _unknownExceptionWhileProcessingReconnection;
|
private static readonly Action<ILogger, int, Exception?> _unknownExceptionWhileProcessingReconnection;
|
||||||
private static readonly Action<ILogger, int, WebSocketError, string?, Exception?> _webSocketErrorCodeAndDetails;
|
private static readonly Action<ILogger, int, WebSocketError, string?, Exception?> _webSocketErrorCodeAndDetails;
|
||||||
private static readonly Action<ILogger, int, string?, Exception?> _webSocketError;
|
private static readonly Action<ILogger, int, string?, Exception?> _webSocketError;
|
||||||
|
private static readonly Action<ILogger, int, int, Exception?> _messageSentNotPending;
|
||||||
private static readonly Action<ILogger, int, string, Exception?> _receivedData;
|
private static readonly Action<ILogger, int, string, Exception?> _receivedData;
|
||||||
private static readonly Action<ILogger, int, string, Exception?> _failedToParse;
|
private static readonly Action<ILogger, int, string, Exception?> _failedToParse;
|
||||||
private static readonly Action<ILogger, int, string, Exception?> _failedToEvaluateMessage;
|
private static readonly Action<ILogger, int, string, Exception?> _failedToEvaluateMessage;
|
||||||
@ -71,6 +72,11 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
new EventId(2005, "WebSocketError"),
|
new EventId(2005, "WebSocketError"),
|
||||||
"[Sckt {SocketId}] error: {ErrorMessage}");
|
"[Sckt {SocketId}] error: {ErrorMessage}");
|
||||||
|
|
||||||
|
_messageSentNotPending = LoggerMessage.Define<int, int>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
new EventId(2006, "MessageSentNotPending"),
|
||||||
|
"[Sckt {SocketId}] [Req {RequestId}] message sent, but not pending");
|
||||||
|
|
||||||
_receivedData = LoggerMessage.Define<int, string>(
|
_receivedData = LoggerMessage.Define<int, string>(
|
||||||
LogLevel.Trace,
|
LogLevel.Trace,
|
||||||
new EventId(2007, "ReceivedData"),
|
new EventId(2007, "ReceivedData"),
|
||||||
@ -228,6 +234,11 @@ namespace CryptoExchange.Net.Logging.Extensions
|
|||||||
_webSocketError(logger, socketId, errorMessage, e);
|
_webSocketError(logger, socketId, errorMessage, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MessageSentNotPending(this ILogger logger, int socketId, int requestId)
|
||||||
|
{
|
||||||
|
_messageSentNotPending(logger, socketId, requestId, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static void ReceivedData(this ILogger logger, int socketId, string originalData)
|
public static void ReceivedData(this ILogger logger, int socketId, string originalData)
|
||||||
{
|
{
|
||||||
_receivedData(logger, socketId, originalData, null);
|
_receivedData(logger, socketId, originalData, null);
|
||||||
|
|||||||
@ -9,10 +9,6 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AssetAlias
|
public class AssetAlias
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Alias type
|
|
||||||
/// </summary>
|
|
||||||
public AliasType Type { get; set; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the asset on the exchange
|
/// The name of the asset on the exchange
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -25,26 +21,10 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AssetAlias(string exchangeName, string commonName, AliasType type = AliasType.BothWays)
|
public AssetAlias(string exchangeName, string commonName)
|
||||||
{
|
{
|
||||||
ExchangeAssetName = exchangeName;
|
ExchangeAssetName = exchangeName;
|
||||||
CommonAssetName = commonName;
|
CommonAssetName = commonName;
|
||||||
Type = type;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Alias type
|
|
||||||
/// </summary>
|
|
||||||
public enum AliasType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Translate both from and to exchange
|
|
||||||
/// </summary>
|
|
||||||
BothWays,
|
|
||||||
/// <summary>
|
|
||||||
/// Only translate when converting to exchange
|
|
||||||
/// </summary>
|
|
||||||
OnlyToExchange
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,22 +23,12 @@ namespace CryptoExchange.Net.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map the common name to an exchange name for an asset. If there is no alias the input name is returned
|
/// Map the common name to an exchange name for an asset. If there is no alias the input name is returned
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CommonToExchangeName(string commonName) => !AutoConvertEnabled ? commonName : Aliases.FirstOrDefault(x => x.CommonAssetName == commonName)?.ExchangeAssetName ?? commonName;
|
public string CommonToExchangeName(string commonName) => !AutoConvertEnabled ? commonName : Aliases.SingleOrDefault(x => x.CommonAssetName == commonName)?.ExchangeAssetName ?? commonName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map the exchange name to a common name for an asset. If there is no alias the input name is returned
|
/// Map the exchange name to a common name for an asset. If there is no alias the input name is returned
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ExchangeToCommonName(string exchangeName)
|
public string ExchangeToCommonName(string exchangeName) => !AutoConvertEnabled ? exchangeName : Aliases.SingleOrDefault(x => x.ExchangeAssetName == exchangeName)?.CommonAssetName ?? exchangeName;
|
||||||
{
|
|
||||||
if (!AutoConvertEnabled)
|
|
||||||
return exchangeName;
|
|
||||||
|
|
||||||
var alias = Aliases.FirstOrDefault(x => x.ExchangeAssetName == exchangeName);
|
|
||||||
if (alias == null || alias.Type == AliasType.OnlyToExchange)
|
|
||||||
return exchangeName;
|
|
||||||
|
|
||||||
return alias.CommonAssetName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,31 +267,4 @@ namespace CryptoExchange.Net.Objects
|
|||||||
Succeed
|
Succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Subscription status
|
|
||||||
/// </summary>
|
|
||||||
public enum SubscriptionStatus
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Pending, waiting before (re)subscription can be started
|
|
||||||
/// </summary>
|
|
||||||
Pending,
|
|
||||||
/// <summary>
|
|
||||||
/// Currently (re)subscribing, will start producing updates soon if subscription is successful
|
|
||||||
/// </summary>
|
|
||||||
Subscribing,
|
|
||||||
/// <summary>
|
|
||||||
/// Subscribed and listening to updates
|
|
||||||
/// </summary>
|
|
||||||
Subscribed,
|
|
||||||
/// <summary>
|
|
||||||
/// Subscription is being closed and will stop producing updates
|
|
||||||
/// </summary>
|
|
||||||
Closing,
|
|
||||||
/// <summary>
|
|
||||||
/// Subscription is closed and will no long produce updates
|
|
||||||
/// </summary>
|
|
||||||
Closed
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
private readonly Subscription _listener;
|
private readonly Subscription _listener;
|
||||||
|
|
||||||
private object _eventLock = new object();
|
private object _eventLock = new object();
|
||||||
private bool _connectionEventsSubscribed = true;
|
|
||||||
private List<Action> _connectionClosedEventHandlers = new List<Action>();
|
private List<Action> _connectionClosedEventHandlers = new List<Action>();
|
||||||
private List<Action> _connectionLostEventHandlers = new List<Action>();
|
private List<Action> _connectionLostEventHandlers = new List<Action>();
|
||||||
private List<Action<Error>> _resubscribeFailedEventHandlers = new List<Action<Error>>();
|
private List<Action<Error>> _resubscribeFailedEventHandlers = new List<Action<Error>>();
|
||||||
@ -23,11 +22,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
private List<Action> _activityPausedEventHandlers = new List<Action>();
|
private List<Action> _activityPausedEventHandlers = new List<Action>();
|
||||||
private List<Action> _activityUnpausedEventHandlers = new List<Action>();
|
private List<Action> _activityUnpausedEventHandlers = new List<Action>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event when the status of the subscription changes
|
|
||||||
/// </summary>
|
|
||||||
public event Action<SubscriptionStatus>? SubscriptionStatusChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event when the connection is lost. The socket will automatically reconnect when possible.
|
/// Event when the connection is lost. The socket will automatically reconnect when possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -119,34 +113,21 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
_connection.ActivityUnpaused += HandleUnpausedEvent;
|
_connection.ActivityUnpaused += HandleUnpausedEvent;
|
||||||
|
|
||||||
_listener = subscription;
|
_listener = subscription;
|
||||||
_listener.StatusChanged += (x) => SubscriptionStatusChanged?.Invoke(x);
|
_listener.Unsubscribed += HandleUnsubscribed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnsubscribeConnectionEvents()
|
private void HandleUnsubscribed()
|
||||||
{
|
{
|
||||||
lock (_eventLock)
|
_connection.ConnectionClosed -= HandleConnectionClosedEvent;
|
||||||
{
|
_connection.ConnectionLost -= HandleConnectionLostEvent;
|
||||||
if (!_connectionEventsSubscribed)
|
_connection.ConnectionRestored -= HandleConnectionRestoredEvent;
|
||||||
return;
|
_connection.ResubscribingFailed -= HandleResubscribeFailedEvent;
|
||||||
|
_connection.ActivityPaused -= HandlePausedEvent;
|
||||||
_connection.ConnectionClosed -= HandleConnectionClosedEvent;
|
_connection.ActivityUnpaused -= HandleUnpausedEvent;
|
||||||
_connection.ConnectionLost -= HandleConnectionLostEvent;
|
|
||||||
_connection.ConnectionRestored -= HandleConnectionRestoredEvent;
|
|
||||||
_connection.ResubscribingFailed -= HandleResubscribeFailedEvent;
|
|
||||||
_connection.ActivityPaused -= HandlePausedEvent;
|
|
||||||
_connection.ActivityUnpaused -= HandleUnpausedEvent;
|
|
||||||
_connectionEventsSubscribed = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleConnectionClosedEvent()
|
private void HandleConnectionClosedEvent()
|
||||||
{
|
{
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
|
|
||||||
// If we're not the subscription closing this connection don't bother emitting
|
|
||||||
if (!_listener.IsClosingConnection)
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<Action> handlers;
|
List<Action> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _connectionClosedEventHandlers.ToList();
|
handlers = _connectionClosedEventHandlers.ToList();
|
||||||
@ -157,12 +138,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
|
|
||||||
private void HandleConnectionLostEvent()
|
private void HandleConnectionLostEvent()
|
||||||
{
|
{
|
||||||
if (!_listener.Active)
|
|
||||||
{
|
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Action> handlers;
|
List<Action> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _connectionLostEventHandlers.ToList();
|
handlers = _connectionLostEventHandlers.ToList();
|
||||||
@ -173,12 +148,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
|
|
||||||
private void HandleConnectionRestoredEvent(TimeSpan period)
|
private void HandleConnectionRestoredEvent(TimeSpan period)
|
||||||
{
|
{
|
||||||
if (!_listener.Active)
|
|
||||||
{
|
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Action<TimeSpan>> handlers;
|
List<Action<TimeSpan>> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _connectionRestoredEventHandlers.ToList();
|
handlers = _connectionRestoredEventHandlers.ToList();
|
||||||
@ -189,12 +158,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
|
|
||||||
private void HandleResubscribeFailedEvent(Error error)
|
private void HandleResubscribeFailedEvent(Error error)
|
||||||
{
|
{
|
||||||
if (!_listener.Active)
|
|
||||||
{
|
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Action<Error>> handlers;
|
List<Action<Error>> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _resubscribeFailedEventHandlers.ToList();
|
handlers = _resubscribeFailedEventHandlers.ToList();
|
||||||
@ -205,12 +168,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
|
|
||||||
private void HandlePausedEvent()
|
private void HandlePausedEvent()
|
||||||
{
|
{
|
||||||
if (!_listener.Active)
|
|
||||||
{
|
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Action> handlers;
|
List<Action> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _activityPausedEventHandlers.ToList();
|
handlers = _activityPausedEventHandlers.ToList();
|
||||||
@ -221,12 +178,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
|
|
||||||
private void HandleUnpausedEvent()
|
private void HandleUnpausedEvent()
|
||||||
{
|
{
|
||||||
if (!_listener.Active)
|
|
||||||
{
|
|
||||||
UnsubscribeConnectionEvents();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Action> handlers;
|
List<Action> handlers;
|
||||||
lock (_eventLock)
|
lock (_eventLock)
|
||||||
handlers = _activityUnpausedEventHandlers.ToList();
|
handlers = _activityUnpausedEventHandlers.ToList();
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Account type
|
|
||||||
/// </summary>
|
|
||||||
public enum SharedAccountType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Unified account, combined account for multiple different types of trading
|
|
||||||
/// </summary>
|
|
||||||
Unified,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Funding account, where withdrawals and deposits are made from and to
|
|
||||||
/// </summary>
|
|
||||||
Funding,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Spot trading account
|
|
||||||
/// </summary>
|
|
||||||
Spot,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Cross margin account
|
|
||||||
/// </summary>
|
|
||||||
CrossMargin,
|
|
||||||
/// <summary>
|
|
||||||
/// Isolated margin account
|
|
||||||
/// </summary>
|
|
||||||
IsolatedMargin,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Perpetual linear futures account
|
|
||||||
/// </summary>
|
|
||||||
PerpetualLinearFutures,
|
|
||||||
/// <summary>
|
|
||||||
/// Delivery linear futures account
|
|
||||||
/// </summary>
|
|
||||||
DeliveryLinearFutures,
|
|
||||||
/// <summary>
|
|
||||||
/// Perpetual inverse futures account
|
|
||||||
/// </summary>
|
|
||||||
PerpetualInverseFutures,
|
|
||||||
/// <summary>
|
|
||||||
/// Delivery inverse futures account
|
|
||||||
/// </summary>
|
|
||||||
DeliveryInverseFutures,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Option account
|
|
||||||
/// </summary>
|
|
||||||
Option,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Other
|
|
||||||
/// </summary>
|
|
||||||
Other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,7 +12,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Balances request options
|
/// Balances request options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
GetBalancesOptions GetBalancesOptions { get; }
|
EndpointOptions<GetBalancesRequest> GetBalancesOptions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get balances for the user
|
/// Get balances for the user
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Client for transferring funds between account types
|
|
||||||
/// </summary>
|
|
||||||
public interface ITransferRestClient : ISharedClient
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Transfer request options
|
|
||||||
/// </summary>
|
|
||||||
TransferOptions TransferOptions { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Transfer funds between account types
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">Request info</param>
|
|
||||||
/// <param name="ct">Cancellation token</param>
|
|
||||||
Task<ExchangeWebResult<SharedId>> TransferAsync(TransferRequest request, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Options for requesting a transfer
|
|
||||||
/// </summary>
|
|
||||||
public class GetBalancesOptions : EndpointOptions<GetBalancesRequest>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Supported account types
|
|
||||||
/// </summary>
|
|
||||||
public AccountTypeFilter[] SupportedAccountTypes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
public GetBalancesOptions(params AccountTypeFilter[] accountTypes) : base(true)
|
|
||||||
{
|
|
||||||
SupportedAccountTypes = accountTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validate a request
|
|
||||||
/// </summary>
|
|
||||||
public Error? ValidateRequest(
|
|
||||||
string exchange,
|
|
||||||
GetBalancesRequest request,
|
|
||||||
TradingMode[] supportedApiTypes)
|
|
||||||
{
|
|
||||||
if (request.AccountType != null && !IsValid(request.AccountType.Value))
|
|
||||||
return ArgumentError.Invalid(nameof(request.AccountType), "Invalid AccountType");
|
|
||||||
|
|
||||||
return base.ValidateRequest(exchange, request, null, supportedApiTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is the account type valid for this client
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="accountType"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool IsValid(SharedAccountType accountType)
|
|
||||||
{
|
|
||||||
if (accountType == SharedAccountType.Funding)
|
|
||||||
return SupportedAccountTypes.Contains(AccountTypeFilter.Funding);
|
|
||||||
|
|
||||||
if (accountType == SharedAccountType.Spot)
|
|
||||||
return SupportedAccountTypes.Contains(AccountTypeFilter.Spot);
|
|
||||||
|
|
||||||
if (accountType == SharedAccountType.PerpetualLinearFutures
|
|
||||||
|| accountType == SharedAccountType.PerpetualInverseFutures
|
|
||||||
|| accountType == SharedAccountType.DeliveryLinearFutures
|
|
||||||
|| accountType == SharedAccountType.DeliveryInverseFutures)
|
|
||||||
{
|
|
||||||
return SupportedAccountTypes.Contains(AccountTypeFilter.Futures);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (accountType == SharedAccountType.CrossMargin
|
|
||||||
|| accountType == SharedAccountType.IsolatedMargin)
|
|
||||||
{
|
|
||||||
return SupportedAccountTypes.Contains(AccountTypeFilter.Margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SupportedAccountTypes.Contains(AccountTypeFilter.Option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Account type filter
|
|
||||||
/// </summary>
|
|
||||||
public enum AccountTypeFilter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Funding account
|
|
||||||
/// </summary>
|
|
||||||
Funding,
|
|
||||||
/// <summary>
|
|
||||||
/// Spot account
|
|
||||||
/// </summary>
|
|
||||||
Spot,
|
|
||||||
/// <summary>
|
|
||||||
/// Futures account
|
|
||||||
/// </summary>
|
|
||||||
Futures,
|
|
||||||
/// <summary>
|
|
||||||
/// Margin account
|
|
||||||
/// </summary>
|
|
||||||
Margin,
|
|
||||||
/// <summary>
|
|
||||||
/// Option account
|
|
||||||
/// </summary>
|
|
||||||
Option
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
using CryptoExchange.Net.Objects;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace CryptoExchange.Net.SharedApis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Options for requesting a transfer
|
|
||||||
/// </summary>
|
|
||||||
public class TransferOptions : EndpointOptions<TransferRequest>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Supported account types
|
|
||||||
/// </summary>
|
|
||||||
public SharedAccountType[] SupportedAccountTypes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
public TransferOptions(SharedAccountType[] accountTypes) : base(true)
|
|
||||||
{
|
|
||||||
SupportedAccountTypes = accountTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validate a request
|
|
||||||
/// </summary>
|
|
||||||
public new Error? ValidateRequest(
|
|
||||||
string exchange,
|
|
||||||
TransferRequest request,
|
|
||||||
TradingMode? tradingMode,
|
|
||||||
TradingMode[] supportedApiTypes)
|
|
||||||
{
|
|
||||||
if (!SupportedAccountTypes.Contains(request.FromAccountType))
|
|
||||||
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid FromAccountType");
|
|
||||||
|
|
||||||
if (!SupportedAccountTypes.Contains(request.ToAccountType))
|
|
||||||
return ArgumentError.Invalid(nameof(request.FromAccountType), "Invalid ToAccountType");
|
|
||||||
|
|
||||||
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -8,28 +8,18 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
public record GetBalancesRequest : SharedRequest
|
public record GetBalancesRequest : SharedRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Account type
|
/// Trading mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedAccountType? AccountType { get; set; }
|
public TradingMode? TradingMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tradingMode">Trading mode</param>
|
/// <param name="tradingMode">Trading mode</param>
|
||||||
/// <param name="exchangeParameters">Exchange specific parameters</param>
|
/// <param name="exchangeParameters">Exchange specific parameters</param>
|
||||||
public GetBalancesRequest(TradingMode tradingMode, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
|
public GetBalancesRequest(TradingMode? tradingMode = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
|
||||||
{
|
{
|
||||||
AccountType = tradingMode.ToAccountType();
|
TradingMode = tradingMode;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="accountType">Account type</param>
|
|
||||||
/// <param name="exchangeParameters">Exchange specific parameters</param>
|
|
||||||
public GetBalancesRequest(SharedAccountType? accountType = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
|
|
||||||
{
|
|
||||||
AccountType = accountType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,61 +0,0 @@
|
|||||||
namespace CryptoExchange.Net.SharedApis
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Request to transfer funds between account types
|
|
||||||
/// </summary>
|
|
||||||
public record TransferRequest : SharedRequest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Asset
|
|
||||||
/// </summary>
|
|
||||||
public string Asset { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Quantity
|
|
||||||
/// </summary>
|
|
||||||
public decimal Quantity { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// From symbol
|
|
||||||
/// </summary>
|
|
||||||
public string? FromSymbol { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// To symbol
|
|
||||||
/// </summary>
|
|
||||||
public string? ToSymbol { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// From account type
|
|
||||||
/// </summary>
|
|
||||||
public SharedAccountType FromAccountType { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// To account type
|
|
||||||
/// </summary>
|
|
||||||
public SharedAccountType ToAccountType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ctor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="asset">The asset to transfer</param>
|
|
||||||
/// <param name="quantity">Quantity to transfer</param>
|
|
||||||
/// <param name="fromAccount">From account type</param>
|
|
||||||
/// <param name="toAccount">To account type</param>
|
|
||||||
/// <param name="fromSymbol">From symbol</param>
|
|
||||||
/// <param name="toSymbol">To symbol</param>
|
|
||||||
/// <param name="exchangeParameters">Exchange specific parameters</param>
|
|
||||||
public TransferRequest(
|
|
||||||
string asset,
|
|
||||||
decimal quantity,
|
|
||||||
SharedAccountType fromAccount,
|
|
||||||
SharedAccountType toAccount,
|
|
||||||
string? fromSymbol = null,
|
|
||||||
string? toSymbol = null,
|
|
||||||
ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
|
|
||||||
{
|
|
||||||
Asset = asset;
|
|
||||||
Quantity = quantity;
|
|
||||||
FromAccountType = fromAccount;
|
|
||||||
ToAccountType = toAccount;
|
|
||||||
FromSymbol = fromSymbol;
|
|
||||||
ToSymbol = toSymbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,7 +17,6 @@
|
|||||||
/// Total quantity
|
/// Total quantity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Total { get; set; }
|
public decimal Total { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Isolated margin symbol, only applicable for isolated margin futures
|
/// Isolated margin symbol, only applicable for isolated margin futures
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mark/index price kline
|
/// Mark/index price kline
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record SharedFuturesKline : SharedSymbolModel
|
public record SharedFuturesKline
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open time
|
/// Open time
|
||||||
@ -31,8 +31,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedFuturesKline(SharedSymbol? sharedSymbol, string symbol, DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice)
|
public SharedFuturesKline(DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice)
|
||||||
: base(sharedSymbol, symbol)
|
|
||||||
{
|
{
|
||||||
OpenTime = openTime;
|
OpenTime = openTime;
|
||||||
ClosePrice = closePrice;
|
ClosePrice = closePrice;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Kline info
|
/// Kline info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record SharedKline : SharedSymbolModel
|
public record SharedKline
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open time
|
/// Open time
|
||||||
@ -35,8 +35,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedKline(SharedSymbol? sharedSymbol, string symbol, DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice, decimal volume)
|
public SharedKline(DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice, decimal volume)
|
||||||
: base(sharedSymbol, symbol)
|
|
||||||
{
|
{
|
||||||
OpenTime = openTime;
|
OpenTime = openTime;
|
||||||
ClosePrice = closePrice;
|
ClosePrice = closePrice;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Public trade info
|
/// Public trade info
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record SharedTrade : SharedSymbolModel
|
public record SharedTrade
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Quantity of the trade
|
/// Quantity of the trade
|
||||||
@ -27,7 +27,7 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedTrade(SharedSymbol? sharedSymbol, string symbol, decimal quantity, decimal price, DateTime timestamp) : base(sharedSymbol, symbol)
|
public SharedTrade(decimal quantity, decimal price, DateTime timestamp)
|
||||||
{
|
{
|
||||||
Quantity = quantity;
|
Quantity = quantity;
|
||||||
Price = price;
|
Price = price;
|
||||||
|
|||||||
@ -28,10 +28,6 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string OrderId { get; set; }
|
public string OrderId { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The client order id
|
|
||||||
/// </summary>
|
|
||||||
public string? ClientOrderId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Side of the trade
|
/// Side of the trade
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedOrderSide? Side { get; set; }
|
public SharedOrderSide? Side { get; set; }
|
||||||
|
|||||||
@ -33,11 +33,6 @@ namespace CryptoExchange.Net.SharedApis
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? DeliverTime { get; set; }
|
public DateTime? DeliverTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Use this constant to dynamically select a USD or USD equivalent asset (stable coin) supported on the exchange
|
|
||||||
/// </summary>
|
|
||||||
public const string UsdOrStable = "CE-UsdStable";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new SharedSymbol
|
/// Create a new SharedSymbol
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -296,7 +296,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
{
|
{
|
||||||
foreach (var subscription in _listeners.OfType<Subscription>().Where(l => l.UserSubscription && !l.IsClosingConnection))
|
foreach (var subscription in _listeners.OfType<Subscription>().Where(l => l.UserSubscription))
|
||||||
subscription.Reset();
|
subscription.Reset();
|
||||||
|
|
||||||
foreach (var query in _listeners.OfType<Query>().ToList())
|
foreach (var query in _listeners.OfType<Query>().ToList())
|
||||||
@ -449,7 +449,10 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (query == null)
|
if (query == null)
|
||||||
|
{
|
||||||
|
_logger.MessageSentNotPending(SocketId, requestId);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
query.IsSend(query.RequestTimeout ?? ApiClient.ClientOptions.RequestTimeout);
|
query.IsSend(query.RequestTimeout ?? ApiClient.ClientOptions.RequestTimeout);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -524,10 +527,10 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processor is Subscription subscriptionProcessor && subscriptionProcessor.Status == SubscriptionStatus.Subscribing)
|
if (processor is Subscription subscriptionProcessor && !subscriptionProcessor.Confirmed)
|
||||||
{
|
{
|
||||||
// If this message is for this listener then it is automatically confirmed, even if the subscription is not (yet) confirmed
|
// If this message is for this listener then it is automatically confirmed, even if the subscription is not (yet) confirmed
|
||||||
subscriptionProcessor.Status = SubscriptionStatus.Subscribed;
|
subscriptionProcessor.Confirmed = true;
|
||||||
if (subscriptionProcessor.SubscriptionQuery?.TimeoutBehavior == TimeoutBehavior.Succeed)
|
if (subscriptionProcessor.SubscriptionQuery?.TimeoutBehavior == TimeoutBehavior.Succeed)
|
||||||
// If this subscription has a query waiting for a timeout (success if there is no error response)
|
// If this subscription has a query waiting for a timeout (success if there is no error response)
|
||||||
// then time it out now as the data is being received, so we assume it's successful
|
// then time it out now as the data is being received, so we assume it's successful
|
||||||
@ -654,16 +657,13 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
public async Task CloseAsync(Subscription subscription)
|
public async Task CloseAsync(Subscription subscription)
|
||||||
{
|
{
|
||||||
// If we are resubscribing this subscription at this moment we'll want to wait for a bit until it is finished to avoid concurrency issues
|
// If we are resubscribing this subscription at this moment we'll want to wait for a bit until it is finished to avoid concurrency issues
|
||||||
while (subscription.Status == SubscriptionStatus.Subscribing)
|
while (subscription.IsResubscribing)
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
await Task.Delay(50).ConfigureAwait(false);
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Closing;
|
subscription.Closed = true;
|
||||||
|
|
||||||
if (Status == SocketStatus.Closing || Status == SocketStatus.Closed || Status == SocketStatus.Disposed)
|
if (Status == SocketStatus.Closing || Status == SocketStatus.Closed || Status == SocketStatus.Disposed)
|
||||||
{
|
|
||||||
subscription.Status = SubscriptionStatus.Closed;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
_logger.ClosingSubscription(SocketId, subscription.Id);
|
_logger.ClosingSubscription(SocketId, subscription.Id);
|
||||||
if (subscription.CancellationTokenRegistration.HasValue)
|
if (subscription.CancellationTokenRegistration.HasValue)
|
||||||
@ -675,7 +675,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
bool shouldCloseConnection;
|
bool shouldCloseConnection;
|
||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
shouldCloseConnection = _listeners.OfType<Subscription>().All(r => !r.UserSubscription || r.Status == SubscriptionStatus.Closing || r.Status == SubscriptionStatus.Closed) && !DedicatedRequestConnection.IsDedicatedRequestConnection;
|
shouldCloseConnection = _listeners.OfType<Subscription>().All(r => !r.UserSubscription || r.Closed) && !DedicatedRequestConnection.IsDedicatedRequestConnection;
|
||||||
|
|
||||||
if (!anyDuplicateSubscription)
|
if (!anyDuplicateSubscription)
|
||||||
{
|
{
|
||||||
@ -693,7 +693,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
if (Status == SocketStatus.Closing)
|
if (Status == SocketStatus.Closing)
|
||||||
{
|
{
|
||||||
subscription.Status = SubscriptionStatus.Closed;
|
|
||||||
_logger.AlreadyClosing(SocketId);
|
_logger.AlreadyClosing(SocketId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -701,7 +700,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
if (shouldCloseConnection)
|
if (shouldCloseConnection)
|
||||||
{
|
{
|
||||||
Status = SocketStatus.Closing;
|
Status = SocketStatus.Closing;
|
||||||
subscription.IsClosingConnection = true;
|
|
||||||
_logger.ClosingNoMoreSubscriptions(SocketId);
|
_logger.ClosingNoMoreSubscriptions(SocketId);
|
||||||
await CloseAsync().ConfigureAwait(false);
|
await CloseAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -709,7 +707,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
_listeners.Remove(subscription);
|
_listeners.Remove(subscription);
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Closed;
|
subscription.InvokeUnsubscribedHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -993,7 +991,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
List<Subscription> subList;
|
List<Subscription> subList;
|
||||||
lock (_listenersLock)
|
lock (_listenersLock)
|
||||||
subList = _listeners.OfType<Subscription>().Where(x => x.Active).Skip(batch * batchSize).Take(batchSize).ToList();
|
subList = _listeners.OfType<Subscription>().Where(x => !x.Closed).Skip(batch * batchSize).Take(batchSize).ToList();
|
||||||
|
|
||||||
if (subList.Count == 0)
|
if (subList.Count == 0)
|
||||||
break;
|
break;
|
||||||
@ -1002,32 +1000,34 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
foreach (var subscription in subList)
|
foreach (var subscription in subList)
|
||||||
{
|
{
|
||||||
subscription.ConnectionInvocations = 0;
|
subscription.ConnectionInvocations = 0;
|
||||||
if (!subscription.Active)
|
if (subscription.Closed)
|
||||||
// Can be closed during resubscribing
|
// Can be closed during resubscribing
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
subscription.Status = SubscriptionStatus.Subscribing;
|
subscription.IsResubscribing = true;
|
||||||
var result = await ApiClient.RevitalizeRequestAsync(subscription).ConfigureAwait(false);
|
var result = await ApiClient.RevitalizeRequestAsync(subscription).ConfigureAwait(false);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
_logger.FailedRequestRevitalization(SocketId, result.Error?.ToString());
|
_logger.FailedRequestRevitalization(SocketId, result.Error?.ToString());
|
||||||
subscription.Status = SubscriptionStatus.Pending;
|
subscription.IsResubscribing = false;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var subQuery = subscription.CreateSubscriptionQuery(this);
|
var subQuery = subscription.CreateSubscriptionQuery(this);
|
||||||
if (subQuery == null)
|
if (subQuery == null)
|
||||||
{
|
{
|
||||||
subscription.Status = SubscriptionStatus.Subscribed;
|
subscription.IsResubscribing = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var waitEvent = new AsyncResetEvent(false);
|
var waitEvent = new AsyncResetEvent(false);
|
||||||
taskList.Add(SendAndWaitQueryAsync(subQuery, waitEvent).ContinueWith((r) =>
|
taskList.Add(SendAndWaitQueryAsync(subQuery, waitEvent).ContinueWith((r) =>
|
||||||
{
|
{
|
||||||
subscription.Status = r.Result.Success ? SubscriptionStatus.Subscribed: SubscriptionStatus.Pending;
|
subscription.IsResubscribing = false;
|
||||||
subscription.HandleSubQueryResponse(subQuery.Response!);
|
subscription.HandleSubQueryResponse(subQuery.Response!);
|
||||||
waitEvent.Set();
|
waitEvent.Set();
|
||||||
|
if (r.Result.Success)
|
||||||
|
subscription.Confirmed = true;
|
||||||
return r.Result;
|
return r.Result;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,32 +35,20 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UserSubscription { get; set; }
|
public bool UserSubscription { get; set; }
|
||||||
|
|
||||||
private SubscriptionStatus _status;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current subscription status
|
/// Has the subscription been confirmed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SubscriptionStatus Status
|
public bool Confirmed { get; set; }
|
||||||
{
|
|
||||||
get => _status;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_status == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_status = value;
|
|
||||||
Task.Run(() => StatusChanged?.Invoke(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the subscription is active
|
/// Is the subscription closed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Active => Status != SubscriptionStatus.Closing && Status != SubscriptionStatus.Closed;
|
public bool Closed { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the unsubscribing of this subscription lead to the closing of the connection
|
/// Is the subscription currently resubscribing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsClosingConnection { get; set; }
|
public bool IsResubscribing { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logger
|
/// Logger
|
||||||
@ -89,7 +77,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Listener unsubscribed event
|
/// Listener unsubscribed event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<SubscriptionStatus>? StatusChanged;
|
public event Action? Unsubscribed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Subscription topic
|
/// Subscription topic
|
||||||
@ -179,7 +167,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
Status = SubscriptionStatus.Pending;
|
Confirmed = false;
|
||||||
DoHandleReset();
|
DoHandleReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,16 +185,24 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
Exception?.Invoke(e);
|
Exception?.Invoke(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke the unsubscribed event
|
||||||
|
/// </summary>
|
||||||
|
public void InvokeUnsubscribedHandler()
|
||||||
|
{
|
||||||
|
Unsubscribed?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// State of this subscription
|
/// State of this subscription
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Id">The id of the subscription</param>
|
/// <param name="Id">The id of the subscription</param>
|
||||||
/// <param name="Status">Subscription status</param>
|
/// <param name="Confirmed">True when the subscription query is handled (either accepted or rejected)</param>
|
||||||
/// <param name="Invocations">Number of times this subscription got a message</param>
|
/// <param name="Invocations">Number of times this subscription got a message</param>
|
||||||
/// <param name="ListenMatcher">Matcher for this subscription</param>
|
/// <param name="ListenMatcher">Matcher for this subscription</param>
|
||||||
public record SubscriptionState(
|
public record SubscriptionState(
|
||||||
int Id,
|
int Id,
|
||||||
SubscriptionStatus Status,
|
bool Confirmed,
|
||||||
int Invocations,
|
int Invocations,
|
||||||
MessageMatcher ListenMatcher
|
MessageMatcher ListenMatcher
|
||||||
);
|
);
|
||||||
@ -217,7 +213,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public SubscriptionState GetState()
|
public SubscriptionState GetState()
|
||||||
{
|
{
|
||||||
return new SubscriptionState(Id, Status, TotalInvocations, MessageMatcher);
|
return new SubscriptionState(Id, Confirmed, TotalInvocations, MessageMatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// <param name="authenticated"></param>
|
/// <param name="authenticated"></param>
|
||||||
public SystemSubscription(ILogger logger, bool authenticated = false) : base(logger, authenticated, false)
|
public SystemSubscription(ILogger logger, bool authenticated = false) : base(logger, authenticated, false)
|
||||||
{
|
{
|
||||||
Status = SubscriptionStatus.Subscribed;
|
Confirmed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -415,7 +415,7 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
var value = jsonValue.GetDecimal();
|
var value = jsonValue.GetDecimal();
|
||||||
if (objectValue is DateTime time)
|
if (objectValue is DateTime time)
|
||||||
{
|
{
|
||||||
if (time != DateTimeConverter.ParseFromDecimal(value))
|
if (time != DateTimeConverter.ParseFromDouble((double)value))
|
||||||
throw new Exception($"{method}: {property} not equal: {DateTimeConverter.ParseFromDouble((double)value!)} vs {time}");
|
throw new Exception($"{method}: {property} not equal: {DateTimeConverter.ParseFromDouble((double)value!)} vs {time}");
|
||||||
}
|
}
|
||||||
else if (propertyType.IsEnum || Nullable.GetUnderlyingType(propertyType)?.IsEnum == true)
|
else if (propertyType.IsEnum || Nullable.GetUnderlyingType(propertyType)?.IsEnum == true)
|
||||||
|
|||||||
@ -325,12 +325,9 @@ namespace CryptoExchange.Net.Trackers.Trades
|
|||||||
if (Period != null)
|
if (Period != null)
|
||||||
items = items.Where(e => e.Timestamp >= DateTime.UtcNow.Add(-Period.Value));
|
items = items.Where(e => e.Timestamp >= DateTime.UtcNow.Add(-Period.Value));
|
||||||
|
|
||||||
if (items.Any())
|
_snapshotId = data.Max(d => d.Timestamp.Ticks);
|
||||||
{
|
foreach (var item in items.OrderBy(d => d.Timestamp))
|
||||||
_snapshotId = data.Max(d => d.Timestamp.Ticks);
|
_data.Add(item);
|
||||||
foreach (var item in items.OrderBy(d => d.Timestamp))
|
|
||||||
_data.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
_snapshotSet = true;
|
_snapshotSet = true;
|
||||||
_changed = true;
|
_changed = true;
|
||||||
|
|||||||
@ -5,32 +5,30 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="11.10.0" />
|
<PackageReference Include="Binance.Net" Version="11.7.1" />
|
||||||
<PackageReference Include="Bitfinex.Net" Version="9.10.0" />
|
<PackageReference Include="Bitfinex.Net" Version="9.7.0" />
|
||||||
<PackageReference Include="BitMart.Net" Version="2.11.0" />
|
<PackageReference Include="BitMart.Net" Version="2.8.0" />
|
||||||
<PackageReference Include="BloFin.Net" Version="1.3.0" />
|
<PackageReference Include="BloFin.Net" Version="1.0.0" />
|
||||||
<PackageReference Include="Bybit.Net" Version="5.12.0" />
|
<PackageReference Include="Bybit.Net" Version="5.8.0" />
|
||||||
<PackageReference Include="CoinEx.Net" Version="9.10.0" />
|
<PackageReference Include="CoinEx.Net" Version="9.7.0" />
|
||||||
<PackageReference Include="CoinW.Net" Version="1.7.0" />
|
<PackageReference Include="CoinW.Net" Version="1.4.0" />
|
||||||
<PackageReference Include="CryptoCom.Net" Version="2.11.0" />
|
<PackageReference Include="CryptoCom.Net" Version="2.8.0" />
|
||||||
<PackageReference Include="DeepCoin.Net" Version="2.10.0" />
|
<PackageReference Include="DeepCoin.Net" Version="2.7.0" />
|
||||||
<PackageReference Include="GateIo.Net" Version="2.12.0" />
|
<PackageReference Include="GateIo.Net" Version="2.8.1" />
|
||||||
<PackageReference Include="HyperLiquid.Net" Version="2.16.0" />
|
<PackageReference Include="HyperLiquid.Net" Version="2.12.0" />
|
||||||
<PackageReference Include="JK.BingX.Net" Version="2.10.0" />
|
<PackageReference Include="JK.BingX.Net" Version="2.7.0" />
|
||||||
<PackageReference Include="JK.Bitget.Net" Version="2.11.0" />
|
<PackageReference Include="JK.Bitget.Net" Version="2.7.1" />
|
||||||
<PackageReference Include="JK.Mexc.Net" Version="3.11.0" />
|
<PackageReference Include="JK.Mexc.Net" Version="3.8.0" />
|
||||||
<PackageReference Include="JK.OKX.Net" Version="3.10.0" />
|
<PackageReference Include="JK.OKX.Net" Version="3.7.1" />
|
||||||
<PackageReference Include="Jkorf.Aster.Net" Version="1.2.0" />
|
<PackageReference Include="JKorf.BitMEX.Net" Version="2.7.0" />
|
||||||
<PackageReference Include="JKorf.BitMEX.Net" Version="2.10.0" />
|
<PackageReference Include="JKorf.Coinbase.Net" Version="2.7.0" />
|
||||||
<PackageReference Include="JKorf.Coinbase.Net" Version="2.10.0" />
|
<PackageReference Include="JKorf.HTX.Net" Version="7.7.0" />
|
||||||
<PackageReference Include="JKorf.HTX.Net" Version="7.10.0" />
|
<PackageReference Include="KrakenExchange.Net" Version="6.7.0" />
|
||||||
<PackageReference Include="JKorf.Upbit.Net" Version="1.1.0" />
|
<PackageReference Include="Kucoin.Net" Version="7.7.1" />
|
||||||
<PackageReference Include="KrakenExchange.Net" Version="6.10.0" />
|
|
||||||
<PackageReference Include="Kucoin.Net" Version="7.10.0" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Toobit.Net" Version="1.9.0" />
|
<PackageReference Include="Toobit.Net" Version="1.6.0" />
|
||||||
<PackageReference Include="WhiteBit.Net" Version="2.11.0" />
|
<PackageReference Include="WhiteBit.Net" Version="2.8.0" />
|
||||||
<PackageReference Include="XT.Net" Version="2.10.0" />
|
<PackageReference Include="XT.Net" Version="2.7.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@inject IAsterRestClient asterClient
|
|
||||||
@inject IBinanceRestClient binanceClient
|
@inject IBinanceRestClient binanceClient
|
||||||
@inject IBingXRestClient bingXClient
|
@inject IBingXRestClient bingXClient
|
||||||
@inject IBitfinexRestClient bitfinexClient
|
@inject IBitfinexRestClient bitfinexClient
|
||||||
@ -21,7 +20,6 @@
|
|||||||
@inject IMexcRestClient mexcClient
|
@inject IMexcRestClient mexcClient
|
||||||
@inject IOKXRestClient okxClient
|
@inject IOKXRestClient okxClient
|
||||||
@inject IToobitRestClient toobitClient
|
@inject IToobitRestClient toobitClient
|
||||||
@inject IUpbitRestClient upbitClient
|
|
||||||
@inject IWhiteBitRestClient whitebitClient
|
@inject IWhiteBitRestClient whitebitClient
|
||||||
@inject IXTRestClient xtClient
|
@inject IXTRestClient xtClient
|
||||||
|
|
||||||
@ -36,7 +34,6 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
var asterTask = asterClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
|
||||||
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var binanceTask = binanceClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
var bingXTask = bingXClient.SpotApi.ExchangeData.GetTickersAsync("BTC-USDT");
|
var bingXTask = bingXClient.SpotApi.ExchangeData.GetTickersAsync("BTC-USDT");
|
||||||
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
|
var bitfinexTask = bitfinexClient.SpotApi.ExchangeData.GetTickerAsync("tBTCUSD");
|
||||||
@ -52,20 +49,16 @@
|
|||||||
var deepCoinTask = deepCoinClient.ExchangeApi.ExchangeData.GetTickersAsync(DeepCoin.Net.Enums.SymbolType.Spot);
|
var deepCoinTask = deepCoinClient.ExchangeApi.ExchangeData.GetTickersAsync(DeepCoin.Net.Enums.SymbolType.Spot);
|
||||||
var gateioTask = gateioClient.SpotApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
var gateioTask = gateioClient.SpotApi.ExchangeData.GetTickersAsync("BTC_USDT");
|
||||||
var htxTask = htxClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
var htxTask = htxClient.SpotApi.ExchangeData.GetTickerAsync("btcusdt");
|
||||||
var hyperLiquidTask = hyperLiquidClient.FuturesApi.ExchangeData.GetExchangeInfoAndTickersAsync();
|
var hyperLiquidTask = hyperLiquidClient.FuturesApi.ExchangeData.GetExchangeInfoAndTickersAsync(); // HyperLiquid does not have BTC spot trading
|
||||||
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
|
var krakenTask = krakenClient.SpotApi.ExchangeData.GetTickerAsync("XBTUSD");
|
||||||
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
var kucoinTask = kucoinClient.SpotApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
||||||
var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
var mexcTask = mexcClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
|
||||||
var okxTask = okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
var okxTask = okxClient.UnifiedApi.ExchangeData.GetTickerAsync("BTC-USDT");
|
||||||
var toobitTask = toobitClient.SpotApi.ExchangeData.GetTickersAsync("BTCUSDT");
|
var toobitTask = toobitClient.SpotApi.ExchangeData.GetTickersAsync("BTCUSDT");
|
||||||
var upbitTask = upbitClient.SpotApi.ExchangeData.GetTickerAsync("USDT-BTC");
|
|
||||||
var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync();
|
var whitebitTask = whitebitClient.V4Api.ExchangeData.GetTickersAsync();
|
||||||
var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt");
|
var xtTask = xtClient.SpotApi.ExchangeData.GetTickersAsync("btc_usdt");
|
||||||
|
|
||||||
await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bitmexTask, bybitTask, coinexTask, coinWTask, deepCoinTask, gateioTask, htxTask, krakenTask, kucoinTask, mexcTask, okxTask);
|
await Task.WhenAll(binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bybitTask, coinexTask, deepCoinTask, gateioTask, htxTask, krakenTask, kucoinTask, mexcTask, okxTask);
|
||||||
|
|
||||||
if (asterTask.Result.Success)
|
|
||||||
_prices.Add("Aster", asterTask.Result.Data.LastPrice);
|
|
||||||
|
|
||||||
if (binanceTask.Result.Success)
|
if (binanceTask.Result.Success)
|
||||||
_prices.Add("Binance", binanceTask.Result.Data.LastPrice);
|
_prices.Add("Binance", binanceTask.Result.Data.LastPrice);
|
||||||
@ -138,9 +131,6 @@
|
|||||||
if (toobitTask.Result.Success)
|
if (toobitTask.Result.Success)
|
||||||
_prices.Add("Toobit", toobitTask.Result.Data.Single().LastPrice ?? 0);
|
_prices.Add("Toobit", toobitTask.Result.Data.Single().LastPrice ?? 0);
|
||||||
|
|
||||||
if (upbitTask.Result.Success)
|
|
||||||
_prices.Add("Upbit", upbitTask.Result.Data.LastPrice ?? 0);
|
|
||||||
|
|
||||||
if (whitebitTask.Result.Success){
|
if (whitebitTask.Result.Success){
|
||||||
// WhiteBit API doesn't offer an endpoint to filter for a specific ticker, so we have to filter client side
|
// WhiteBit API doesn't offer an endpoint to filter for a specific ticker, so we have to filter client side
|
||||||
var tickers = whitebitTask.Result.Data;
|
var tickers = whitebitTask.Result.Data;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
@page "/LiveData"
|
@page "/LiveData"
|
||||||
@inject IAsterSocketClient asterSocketClient
|
|
||||||
@inject IBinanceSocketClient binanceSocketClient
|
@inject IBinanceSocketClient binanceSocketClient
|
||||||
@inject IBingXSocketClient bingXSocketClient
|
@inject IBingXSocketClient bingXSocketClient
|
||||||
@inject IBitfinexSocketClient bitfinexSocketClient
|
@inject IBitfinexSocketClient bitfinexSocketClient
|
||||||
@ -21,7 +20,6 @@
|
|||||||
@inject IMexcSocketClient mexcSocketClient
|
@inject IMexcSocketClient mexcSocketClient
|
||||||
@inject IOKXSocketClient okxSocketClient
|
@inject IOKXSocketClient okxSocketClient
|
||||||
@inject IToobitSocketClient toobitSocketClient
|
@inject IToobitSocketClient toobitSocketClient
|
||||||
@inject IUpbitSocketClient upbitSocketClient
|
|
||||||
@inject IWhiteBitSocketClient whitebitSocketClient
|
@inject IWhiteBitSocketClient whitebitSocketClient
|
||||||
@inject IXTSocketClient xtSocketClient
|
@inject IXTSocketClient xtSocketClient
|
||||||
@using System.Collections.Concurrent
|
@using System.Collections.Concurrent
|
||||||
@ -45,8 +43,6 @@
|
|||||||
{
|
{
|
||||||
var tasks = new Task<CallResult<UpdateSubscription>>[]
|
var tasks = new Task<CallResult<UpdateSubscription>>[]
|
||||||
{
|
{
|
||||||
// Aster doesn't support the ETH/BTC pair
|
|
||||||
//asterSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Aster", data.Data.LastPrice)),
|
|
||||||
binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
|
binanceSocketClient.SpotApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Binance", data.Data.LastPrice)),
|
||||||
bingXSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("BingX", data.Data.LastPrice)),
|
bingXSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("BingX", data.Data.LastPrice)),
|
||||||
bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
|
bitfinexSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("tETHBTC", data => UpdateData("Bitfinex", data.Data.LastPrice)),
|
||||||
@ -73,7 +69,6 @@
|
|||||||
okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("OKX", data.Data.LastPrice ?? 0)),
|
okxSocketClient.UnifiedApi.ExchangeData.SubscribeToTickerUpdatesAsync("ETH-BTC", data => UpdateData("OKX", data.Data.LastPrice ?? 0)),
|
||||||
// Toobit doesn't support the ETH/BTC pair
|
// Toobit doesn't support the ETH/BTC pair
|
||||||
//toobitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Toobit", data.Data.LastPrice ?? 0)),
|
//toobitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHBTC", data => UpdateData("Toobit", data.Data.LastPrice ?? 0)),
|
||||||
upbitSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("BTC-ETH", data => UpdateData("Upbit", data.Data.LastPrice)),
|
|
||||||
whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("WhiteBit", data.Data.Ticker.LastPrice)),
|
whitebitSocketClient.V4Api.SubscribeToTickerUpdatesAsync("ETH_BTC", data => UpdateData("WhiteBit", data.Data.Ticker.LastPrice)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
@page "/OrderBooks"
|
@page "/OrderBooks"
|
||||||
@using System.Collections.Concurrent
|
@using System.Collections.Concurrent
|
||||||
@using System.Timers
|
@using System.Timers
|
||||||
@using Aster.Net.Interfaces
|
|
||||||
@using Binance.Net.Interfaces
|
@using Binance.Net.Interfaces
|
||||||
@using BingX.Net.Interfaces
|
@using BingX.Net.Interfaces
|
||||||
@using Bitfinex.Net.Interfaces
|
@using Bitfinex.Net.Interfaces
|
||||||
@ -25,11 +24,9 @@
|
|||||||
@using Kucoin.Net.Interfaces
|
@using Kucoin.Net.Interfaces
|
||||||
@using Mexc.Net.Interfaces
|
@using Mexc.Net.Interfaces
|
||||||
@using OKX.Net.Interfaces;
|
@using OKX.Net.Interfaces;
|
||||||
@using Upbit.Net.Interfaces;
|
|
||||||
@using Toobit.Net.Interfaces;
|
@using Toobit.Net.Interfaces;
|
||||||
@using WhiteBit.Net.Interfaces
|
@using WhiteBit.Net.Interfaces
|
||||||
@using XT.Net.Interfaces
|
@using XT.Net.Interfaces
|
||||||
@inject IAsterOrderBookFactory asterFactory
|
|
||||||
@inject IBinanceOrderBookFactory binanceFactory
|
@inject IBinanceOrderBookFactory binanceFactory
|
||||||
@inject IBingXOrderBookFactory bingXFactory
|
@inject IBingXOrderBookFactory bingXFactory
|
||||||
@inject IBitfinexOrderBookFactory bitfinexFactory
|
@inject IBitfinexOrderBookFactory bitfinexFactory
|
||||||
@ -51,7 +48,6 @@
|
|||||||
@inject IMexcOrderBookFactory mexcFactory
|
@inject IMexcOrderBookFactory mexcFactory
|
||||||
@inject IOKXOrderBookFactory okxFactory
|
@inject IOKXOrderBookFactory okxFactory
|
||||||
@inject IToobitOrderBookFactory toobitFactory
|
@inject IToobitOrderBookFactory toobitFactory
|
||||||
@inject IUpbitOrderBookFactory upbitFactory
|
|
||||||
@inject IWhiteBitOrderBookFactory whitebitFactory
|
@inject IWhiteBitOrderBookFactory whitebitFactory
|
||||||
@inject IXTOrderBookFactory xtFactory
|
@inject IXTOrderBookFactory xtFactory
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@ -87,7 +83,6 @@
|
|||||||
|
|
||||||
_books = new Dictionary<string, ISymbolOrderBook>
|
_books = new Dictionary<string, ISymbolOrderBook>
|
||||||
{
|
{
|
||||||
{ "Aster", binanceFactory.CreateSpot("ETHUSDT") },
|
|
||||||
{ "Binance", binanceFactory.CreateSpot("ETHBTC") },
|
{ "Binance", binanceFactory.CreateSpot("ETHBTC") },
|
||||||
{ "BingX", bingXFactory.CreateSpot("ETH-BTC") },
|
{ "BingX", bingXFactory.CreateSpot("ETH-BTC") },
|
||||||
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") },
|
{ "Bitfinex", bitfinexFactory.Create("tETHBTC") },
|
||||||
@ -109,7 +104,6 @@
|
|||||||
{ "Mexc", mexcFactory.CreateSpot("ETHBTC") },
|
{ "Mexc", mexcFactory.CreateSpot("ETHBTC") },
|
||||||
{ "OKX", okxFactory.Create("ETH-BTC") },
|
{ "OKX", okxFactory.Create("ETH-BTC") },
|
||||||
{ "Toobit", toobitFactory.CreateSpot("ETHUSDT") },
|
{ "Toobit", toobitFactory.CreateSpot("ETHUSDT") },
|
||||||
{ "Upbit", upbitFactory.CreateSpot("BTC-ETH") },
|
|
||||||
{ "WhiteBit", whitebitFactory.CreateV4("ETH_BTC") },
|
{ "WhiteBit", whitebitFactory.CreateV4("ETH_BTC") },
|
||||||
{ "XT", xtFactory.CreateSpot("eth_btc") },
|
{ "XT", xtFactory.CreateSpot("eth_btc") },
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
@page "/Trackers"
|
@page "/Trackers"
|
||||||
@using System.Collections.Concurrent
|
@using System.Collections.Concurrent
|
||||||
@using System.Timers
|
@using System.Timers
|
||||||
@using Aster.Net.Interfaces
|
|
||||||
@using Binance.Net.Interfaces
|
@using Binance.Net.Interfaces
|
||||||
@using BingX.Net.Interfaces
|
@using BingX.Net.Interfaces
|
||||||
@using Bitfinex.Net.Interfaces
|
@using Bitfinex.Net.Interfaces
|
||||||
@ -26,11 +25,9 @@
|
|||||||
@using Kucoin.Net.Interfaces
|
@using Kucoin.Net.Interfaces
|
||||||
@using Mexc.Net.Interfaces
|
@using Mexc.Net.Interfaces
|
||||||
@using OKX.Net.Interfaces;
|
@using OKX.Net.Interfaces;
|
||||||
@using Upbit.Net.Interfaces;
|
|
||||||
@using Toobit.Net.Interfaces;
|
@using Toobit.Net.Interfaces;
|
||||||
@using WhiteBit.Net.Interfaces
|
@using WhiteBit.Net.Interfaces
|
||||||
@using XT.Net.Interfaces
|
@using XT.Net.Interfaces
|
||||||
@inject IAsterTrackerFactory asterFactory
|
|
||||||
@inject IBinanceTrackerFactory binanceFactory
|
@inject IBinanceTrackerFactory binanceFactory
|
||||||
@inject IBingXTrackerFactory bingXFactory
|
@inject IBingXTrackerFactory bingXFactory
|
||||||
@inject IBitfinexTrackerFactory bitfinexFactory
|
@inject IBitfinexTrackerFactory bitfinexFactory
|
||||||
@ -52,12 +49,11 @@
|
|||||||
@inject IMexcTrackerFactory mexcFactory
|
@inject IMexcTrackerFactory mexcFactory
|
||||||
@inject IOKXTrackerFactory okxFactory
|
@inject IOKXTrackerFactory okxFactory
|
||||||
@inject IToobitTrackerFactory toobitFactory
|
@inject IToobitTrackerFactory toobitFactory
|
||||||
@inject IUpbitTrackerFactory upbitFactory
|
|
||||||
@inject IWhiteBitTrackerFactory whitebitFactory
|
@inject IWhiteBitTrackerFactory whitebitFactory
|
||||||
@inject IXTTrackerFactory xtFactory
|
@inject IXTTrackerFactory xtFactory
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
<h3>Trade Trackers, live updates:</h3>
|
<h3>ETH-BTC trade Trackers, live updates:</h3>
|
||||||
<div style="display:flex; flex-wrap: wrap;">
|
<div style="display:flex; flex-wrap: wrap;">
|
||||||
@foreach (var tracker in _trackers.OrderBy(p => p.Exchange))
|
@foreach (var tracker in _trackers.OrderBy(p => p.Exchange))
|
||||||
{
|
{
|
||||||
@ -77,39 +73,38 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
var symbol = new SharedSymbol(TradingMode.Spot, "BTC", SharedSymbol.UsdOrStable);
|
var usdcSpotSymbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDC");
|
||||||
var futuresSymbol = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", SharedSymbol.UsdOrStable);
|
var usdtSpotSymbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
|
||||||
|
var usdtPerpLinSymbol = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", "USDT");
|
||||||
|
|
||||||
_trackers = new List<ITradeTracker>
|
_trackers = new List<ITradeTracker>
|
||||||
{
|
{
|
||||||
{ asterFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ binanceFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ binanceFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bingXFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bingXFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitfinexFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitfinexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitgetFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitgetFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitmartFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitmartFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bitmexFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bitmexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ bloFinFactory.CreateTradeTracker(usdtPerpLinSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bloFinFactory.CreateTradeTracker(futuresSymbol, period: TimeSpan.FromMinutes(5)) },
|
{ bybitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ bybitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ coinbaseFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ coinbaseFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ coinExFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ coinExFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ coinWFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ coinWFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ cryptocomFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ cryptocomFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ deepCoinFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ deepCoinFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ gateioFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ gateioFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ htxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ htxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ hyperLiquidFactory.CreateTradeTracker(usdcSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ hyperLiquidFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ krakenFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ krakenFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ kucoinFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ kucoinFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ mexcFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ mexcFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ okxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ okxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ toobitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ toobitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ whitebitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ upbitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
{ xtFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
|
||||||
{ whitebitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
|
||||||
{ xtFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await Task.WhenAll(_trackers.Select(b => b.StartAsync(false)));
|
await Task.WhenAll(_trackers.Select(b => b.StartAsync()));
|
||||||
|
|
||||||
// Use a manual update timer so the page isn't refreshed too often
|
// Use a manual update timer so the page isn't refreshed too often
|
||||||
_timer = new Timer(500);
|
_timer = new Timer(500);
|
||||||
|
|||||||
@ -33,7 +33,6 @@ namespace BlazorClient
|
|||||||
restOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
|
restOptions.ApiCredentials = new ApiCredentials("KEY", "SECRET");
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAster();
|
|
||||||
services.AddBingX();
|
services.AddBingX();
|
||||||
services.AddBitfinex();
|
services.AddBitfinex();
|
||||||
services.AddBitget();
|
services.AddBitget();
|
||||||
@ -54,7 +53,6 @@ namespace BlazorClient
|
|||||||
services.AddMexc();
|
services.AddMexc();
|
||||||
services.AddOKX();
|
services.AddOKX();
|
||||||
services.AddToobit();
|
services.AddToobit();
|
||||||
services.AddUpbit();
|
|
||||||
services.AddWhiteBit();
|
services.AddWhiteBit();
|
||||||
services.AddXT();
|
services.AddXT();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using BlazorClient
|
@using BlazorClient
|
||||||
@using BlazorClient.Shared
|
@using BlazorClient.Shared
|
||||||
@using Aster.Net.Interfaces.Clients;
|
|
||||||
@using Binance.Net.Interfaces.Clients;
|
@using Binance.Net.Interfaces.Clients;
|
||||||
@using BingX.Net.Interfaces.Clients;
|
@using BingX.Net.Interfaces.Clients;
|
||||||
@using Bitfinex.Net.Interfaces.Clients;
|
@using Bitfinex.Net.Interfaces.Clients;
|
||||||
@ -29,7 +28,6 @@
|
|||||||
@using Kucoin.Net.Interfaces.Clients;
|
@using Kucoin.Net.Interfaces.Clients;
|
||||||
@using Mexc.Net.Interfaces.Clients;
|
@using Mexc.Net.Interfaces.Clients;
|
||||||
@using OKX.Net.Interfaces.Clients;
|
@using OKX.Net.Interfaces.Clients;
|
||||||
@using Upbit.Net.Interfaces.Clients;
|
|
||||||
@using Toobit.Net.Interfaces.Clients;
|
@using Toobit.Net.Interfaces.Clients;
|
||||||
@using WhiteBit.Net.Interfaces.Clients
|
@using WhiteBit.Net.Interfaces.Clients
|
||||||
@using XT.Net.Interfaces.Clients
|
@using XT.Net.Interfaces.Clients
|
||||||
|
|||||||
@ -6,20 +6,20 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="11.10.0" />
|
<PackageReference Include="Binance.Net" Version="11.1.0" />
|
||||||
<PackageReference Include="Bitfinex.Net" Version="9.10.0" />
|
<PackageReference Include="Bitfinex.Net" Version="9.1.0" />
|
||||||
<PackageReference Include="BitMart.Net" Version="2.11.0" />
|
<PackageReference Include="BitMart.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="Bybit.Net" Version="5.12.0" />
|
<PackageReference Include="Bybit.Net" Version="5.1.0" />
|
||||||
<PackageReference Include="CoinEx.Net" Version="9.10.0" />
|
<PackageReference Include="CoinEx.Net" Version="9.1.0" />
|
||||||
<PackageReference Include="CryptoCom.Net" Version="2.11.0" />
|
<PackageReference Include="CryptoCom.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="GateIo.Net" Version="2.12.0" />
|
<PackageReference Include="GateIo.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="JK.Bitget.Net" Version="2.11.0" />
|
<PackageReference Include="JK.Bitget.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="JK.Mexc.Net" Version="3.11.0" />
|
<PackageReference Include="JK.Mexc.Net" Version="3.1.0" />
|
||||||
<PackageReference Include="JK.OKX.Net" Version="3.10.0" />
|
<PackageReference Include="JK.OKX.Net" Version="3.1.0" />
|
||||||
<PackageReference Include="JKorf.Coinbase.Net" Version="2.10.0" />
|
<PackageReference Include="JKorf.Coinbase.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="JKorf.HTX.Net" Version="7.10.0" />
|
<PackageReference Include="JKorf.HTX.Net" Version="7.1.0" />
|
||||||
<PackageReference Include="KrakenExchange.Net" Version="6.10.0" />
|
<PackageReference Include="KrakenExchange.Net" Version="6.1.0" />
|
||||||
<PackageReference Include="Kucoin.Net" Version="7.10.0" />
|
<PackageReference Include="Kucoin.Net" Version="7.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -8,9 +8,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Binance.Net" Version="11.10.0" />
|
<PackageReference Include="Binance.Net" Version="11.1.0" />
|
||||||
<PackageReference Include="BitMart.Net" Version="2.11.0" />
|
<PackageReference Include="BitMart.Net" Version="2.1.0" />
|
||||||
<PackageReference Include="JK.OKX.Net" Version="3.10.0" />
|
<PackageReference Include="JK.OKX.Net" Version="3.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
24
README.md
24
README.md
@ -12,7 +12,6 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|
|||||||
|
|
||||||
||Exchange|Type|Repository|Nuget|Referral Link|Referral Fee Discount|
|
||Exchange|Type|Repository|Nuget|Referral Link|Referral Fee Discount|
|
||||||
|--|--|--|--|--|--|--|
|
|--|--|--|--|--|--|--|
|
||||||
||Aster|DEX|[JKorf/Aster.Net](https://github.com/JKorf/Aster.Net)|[](https://www.nuget.org/packages/JKorf.Aster.Net)|[Link](https://www.asterdex.com/en/referral/FD2E11)|4%|
|
|
||||||
||Binance|CEX|[JKorf/Binance.Net](https://github.com/JKorf/Binance.Net)|[](https://www.nuget.org/packages/Binance.Net)|[Link](https://accounts.binance.com/register?ref=X5K3F2ZG)|20%|
|
||Binance|CEX|[JKorf/Binance.Net](https://github.com/JKorf/Binance.Net)|[](https://www.nuget.org/packages/Binance.Net)|[Link](https://accounts.binance.com/register?ref=X5K3F2ZG)|20%|
|
||||||
||BingX|CEX|[JKorf/BingX.Net](https://github.com/JKorf/BingX.Net)|[](https://www.nuget.org/packages/JK.BingX.Net)|[Link](https://bingx.com/invite/FFHRJKWG/)|20%|
|
||BingX|CEX|[JKorf/BingX.Net](https://github.com/JKorf/BingX.Net)|[](https://www.nuget.org/packages/JK.BingX.Net)|[Link](https://bingx.com/invite/FFHRJKWG/)|20%|
|
||||||
||Bitfinex|CEX|[JKorf/Bitfinex.Net](https://github.com/JKorf/Bitfinex.Net)|[](https://www.nuget.org/packages/Bitfinex.Net)|-|-|
|
||Bitfinex|CEX|[JKorf/Bitfinex.Net](https://github.com/JKorf/Bitfinex.Net)|[](https://www.nuget.org/packages/Bitfinex.Net)|-|-|
|
||||||
@ -35,7 +34,6 @@ Full list of all libraries part of the CryptoExchange.Net ecosystem. Consider us
|
|||||||
||Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|-|-|
|
||Mexc|CEX|[JKorf/Mexc.Net](https://github.com/JKorf/Mexc.Net)|[](https://www.nuget.org/packages/JK.Mexc.Net)|-|-|
|
||||||
||OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%|
|
||OKX|CEX|[JKorf/OKX.Net](https://github.com/JKorf/OKX.Net)|[](https://www.nuget.org/packages/JK.OKX.Net)|[Link](https://www.okx.com/join/14592495)|20%|
|
||||||
||Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-|
|
||Toobit|CEX|[JKorf/Toobit.Net](https://github.com/JKorf/Toobit.Net)|[](https://www.nuget.org/packages/Toobit.Net)|[Link](https://www.toobit.com/en-US/register?invite_code=zsV19h)|-|
|
||||||
||Upbit|CEX|[JKorf/Upbit.Net](https://github.com/JKorf/Upbit.Net)|[](https://www.nuget.org/packages/JKorf.Upbit.Net)|-|-|
|
|
||||||
||WhiteBit|CEX|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[](https://www.nuget.org/packages/WhiteBit.Net)|[Link](https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf)|-|
|
||WhiteBit|CEX|[JKorf/WhiteBit.Net](https://github.com/JKorf/WhiteBit.Net)|[](https://www.nuget.org/packages/WhiteBit.Net)|[Link](https://whitebit.com/referral/a8e59b59-186c-4662-824c-3095248e0edf)|-|
|
||||||
||XT|CEX|[JKorf/XT.Net](https://github.com/JKorf/XT.Net)|[](https://www.nuget.org/packages/XT.Net)|[Link](https://www.xt.com/ru/accounts/register?ref=CZG39C)|25%|
|
||XT|CEX|[JKorf/XT.Net](https://github.com/JKorf/XT.Net)|[](https://www.nuget.org/packages/XT.Net)|[Link](https://www.xt.com/ru/accounts/register?ref=CZG39C)|25%|
|
||||||
|
|
||||||
@ -66,28 +64,6 @@ 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 9.12.0 - 03 Nov 2025
|
|
||||||
* Added constant for selecting a supported USD asset for use in a Shared API/SharedSymbol
|
|
||||||
* Added AliasType to specify only one way conversion for AssetAliases
|
|
||||||
* Removed some unhelpful verbose logs
|
|
||||||
|
|
||||||
* Version 9.11.0 - 30 Oct 2025
|
|
||||||
* Added StaticLogger to LibraryHelpers, updated warning logging for converters to use StaticLogger
|
|
||||||
* Added client reference helper to LibraryHelpers
|
|
||||||
* Fixed exception when initial trade snapshot has no items in TradeTracker
|
|
||||||
|
|
||||||
* Version 9.10.0 - 15 Oct 2025
|
|
||||||
* Added ITransferRestClient Shared interface
|
|
||||||
* Added ClientOrderId property to SharedUserTrade model
|
|
||||||
* Updated IBalanceRestClient, GetBalancesRequest now mainly works with SharedAccountType type, allowing more options
|
|
||||||
* Updated IBalanceRestClient, GetBalanceOptions now specifies supported account types
|
|
||||||
* Updated DateTimeConverter to work primarily with decimal instead of double to prevent some floating point issues
|
|
||||||
|
|
||||||
* Version 9.9.0 - 06 Oct 2025
|
|
||||||
* Updated socket Subscription status handling
|
|
||||||
* Added SubscriptionStatusChanged event to UpdateSubscription (SubscribeAsync methods reponse)
|
|
||||||
* Fixed timing issue for connection events in UpdateSubscription
|
|
||||||
|
|
||||||
* Version 9.8.0 - 30 Sep 2025
|
* Version 9.8.0 - 30 Sep 2025
|
||||||
* Added ContractAddress to SharedAsset model
|
* Added ContractAddress to SharedAsset model
|
||||||
* Added ITrackerFactory interface
|
* Added ITrackerFactory interface
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user