mirror of
				https://github.com/JKorf/CryptoExchange.Net
				synced 2025-11-04 04:17:32 +00:00 
			
		
		
		
	Compare commits
	
		
			33 Commits
		
	
	
		
			CryptoExch
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7d657dd533 | ||
| 
						 | 
					1bfdec1484 | ||
| 
						 | 
					8d5b6a53f3 | ||
| 
						 | 
					ad97102e7c | ||
| 
						 | 
					d8dc121386 | ||
| 
						 | 
					10c3868c00 | ||
| 
						 | 
					411ba00a82 | ||
| 
						 | 
					c1b2b62dbc | ||
| 
						 | 
					3960cab7a7 | ||
| 
						 | 
					34e9447e55 | ||
| 
						 | 
					9d3295acc7 | ||
| 
						 | 
					995cd3d84c | ||
| 
						 | 
					919cdf0075 | ||
| 
						 | 
					d181c9cfc1 | ||
| 
						 | 
					5943142c44 | ||
| 
						 | 
					dbc430e838 | ||
| 
						 | 
					7413d03d31 | ||
| 
						 | 
					dd60067684 | ||
| 
						 | 
					04e4ddf525 | ||
| 
						 | 
					99bf6d7c75 | ||
| 
						 | 
					99a203933c | ||
| 
						 | 
					b43d2a2040 | ||
| 
						 | 
					ba9c406def | ||
| 
						 | 
					f5f4d50cc9 | ||
| 
						 | 
					f87506b490 | ||
| 
						 | 
					f6f9a53ce5 | ||
| 
						 | 
					61130ef54e | ||
| 
						 | 
					e8bcbd59be | ||
| 
						 | 
					d433ff7475 | ||
| 
						 | 
					71957037d0 | ||
| 
						 | 
					bcdcdbbd4e | ||
| 
						 | 
					1ece13f5bc | ||
| 
						 | 
					da70ba6ec7 | 
@ -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.8.0</PackageVersion>
 | 
					    <PackageVersion>9.12.0</PackageVersion>
 | 
				
			||||||
    <AssemblyVersion>9.8.0</AssemblyVersion>
 | 
					    <AssemblyVersion>9.12.0</AssemblyVersion>
 | 
				
			||||||
    <FileVersion>9.8.0</FileVersion>
 | 
					    <FileVersion>9.12.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.8.0" />
 | 
					    <PackageReference Include="CryptoExchange.Net" Version="9.12.0" />
 | 
				
			||||||
    <PackageReference Include="protobuf-net" Version="3.2.56" />
 | 
					    <PackageReference Include="protobuf-net" Version="3.2.56" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
@ -5,6 +5,21 @@
 | 
				
			|||||||
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="17.14.1"></PackageReference>
 | 
					    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"></PackageReference>
 | 
				
			||||||
    <PackageReference Include="Moq" Version="4.20.72" />
 | 
					    <PackageReference Include="Moq" Version="4.20.72" />
 | 
				
			||||||
    <PackageReference Include="NUnit" Version="4.3.2"></PackageReference>
 | 
					    <PackageReference Include="NUnit" Version="4.4.0"></PackageReference>
 | 
				
			||||||
    <PackageReference Include="NUnit3TestAdapter" Version="5.0.0"></PackageReference>
 | 
					    <PackageReference Include="NUnit3TestAdapter" Version="5.2.0"></PackageReference>
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
				
			|||||||
@ -202,7 +202,7 @@ namespace CryptoExchange.Net.UnitTests
 | 
				
			|||||||
            await sub;
 | 
					            await sub;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // assert
 | 
					            // assert
 | 
				
			||||||
            ClassicAssert.IsFalse(client.SubClient.TestSubscription.Confirmed);
 | 
					            ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [TestCase()]
 | 
					        [TestCase()]
 | 
				
			||||||
@ -225,7 +225,7 @@ namespace CryptoExchange.Net.UnitTests
 | 
				
			|||||||
            await sub;
 | 
					            await sub;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // assert
 | 
					            // assert
 | 
				
			||||||
            Assert.That(client.SubClient.TestSubscription.Confirmed);
 | 
					            Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -102,7 +102,6 @@ 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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -122,7 +121,6 @@ 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,6 +21,8 @@ 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,6 +40,8 @@ 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,6 +269,7 @@ 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)
 | 
				
			||||||
@ -279,7 +280,7 @@ namespace CryptoExchange.Net.Clients
 | 
				
			|||||||
                {
 | 
					                {
 | 
				
			||||||
                    waitEvent?.Set();
 | 
					                    waitEvent?.Set();
 | 
				
			||||||
                    var isTimeout = subResult.Error is CancellationRequestedError;
 | 
					                    var isTimeout = subResult.Error is CancellationRequestedError;
 | 
				
			||||||
                    if (isTimeout && subscription.Confirmed)
 | 
					                    if (isTimeout && subscription.Status == SubscriptionStatus.Subscribed)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        // 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
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -287,6 +288,7 @@ 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!);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -295,7 +297,7 @@ namespace CryptoExchange.Net.Clients
 | 
				
			|||||||
                subscription.HandleSubQueryResponse(subQuery.Response!);
 | 
					                subscription.HandleSubQueryResponse(subQuery.Response!);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            subscription.Confirmed = true;
 | 
					            subscription.Status = SubscriptionStatus.Subscribed;
 | 
				
			||||||
            if (ct != default)
 | 
					            if (ct != default)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                subscription.CancellationTokenRegistration = ct.Register(async () =>
 | 
					                subscription.CancellationTokenRegistration = ct.Register(async () =>
 | 
				
			||||||
@ -847,7 +849,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\tConfirmed: {subState.Confirmed}");
 | 
					                            sb.AppendLine($"\t\t\tStatus: {subState.Status}");
 | 
				
			||||||
                            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,4 +1,5 @@
 | 
				
			|||||||
using System;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Runtime.Serialization;
 | 
					using System.Runtime.Serialization;
 | 
				
			||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
@ -47,7 +48,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
 | 
				
			|||||||
                if (string.IsNullOrEmpty(value))
 | 
					                if (string.IsNullOrEmpty(value))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (typeToConvert == typeof(bool))
 | 
					                    if (typeToConvert == typeof(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");
 | 
					                        LibraryHelpers.StaticLogger?.LogWarning("Received null bool value, but property type is not a nullable bool");
 | 
				
			||||||
                    return default;
 | 
					                    return default;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
using System;
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Diagnostics.CodeAnalysis;
 | 
					using System.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
@ -14,8 +15,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 double _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000d;
 | 
					        private const decimal _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000m;
 | 
				
			||||||
        private const double _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000d / 1000;
 | 
					        private const decimal _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000m / 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public override bool CanConvert(Type typeToConvert)
 | 
					        public override bool CanConvert(Type typeToConvert)
 | 
				
			||||||
@ -39,17 +40,17 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
 | 
				
			|||||||
                if (reader.TokenType == JsonTokenType.Null)
 | 
					                if (reader.TokenType == JsonTokenType.Null)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (typeToConvert == typeof(DateTime))
 | 
					                    if (typeToConvert == typeof(DateTime))
 | 
				
			||||||
                        Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | DateTime value of null, but property is not nullable");
 | 
					                        LibraryHelpers.StaticLogger?.LogWarning("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 longValue = reader.GetDouble();
 | 
					                    var decValue = reader.GetDecimal();
 | 
				
			||||||
                    if (longValue == 0 || longValue < 0)
 | 
					                    if (decValue == 0 || decValue < 0)
 | 
				
			||||||
                        return default;
 | 
					                        return default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return ParseFromDouble(longValue);
 | 
					                    return ParseFromDecimal(decValue);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (reader.TokenType is JsonTokenType.String)
 | 
					                else if (reader.TokenType is JsonTokenType.String)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
@ -57,7 +58,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"
 | 
				
			||||||
                        || double.TryParse(stringValue, out var doubleVal) && doubleVal == 0)
 | 
					                        || decimal.TryParse(stringValue, out var decVal) && decVal == 0)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        return default;
 | 
					                        return default;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@ -88,20 +89,24 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Parse a long value to datetime
 | 
					        /// Parse a double value to datetime
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        /// <param name="longValue"></param>
 | 
					        public static DateTime ParseFromDouble(double value)
 | 
				
			||||||
        /// <returns></returns>
 | 
					            => ParseFromDecimal((decimal)value);
 | 
				
			||||||
        public static DateTime ParseFromDouble(double longValue)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (longValue < 19999999999)
 | 
					 | 
				
			||||||
                return ConvertFromSeconds(longValue);
 | 
					 | 
				
			||||||
            if (longValue < 19999999999999)
 | 
					 | 
				
			||||||
                return ConvertFromMilliseconds(longValue);
 | 
					 | 
				
			||||||
            if (longValue < 19999999999999999)
 | 
					 | 
				
			||||||
                return ConvertFromMicroseconds(longValue);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ConvertFromNanoseconds(longValue);
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Parse a decimal value to datetime
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static DateTime ParseFromDecimal(decimal value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (value < 19999999999)
 | 
				
			||||||
 | 
					                return ConvertFromSeconds(value);
 | 
				
			||||||
 | 
					            if (value < 19999999999999)
 | 
				
			||||||
 | 
					                return ConvertFromMilliseconds(value);
 | 
				
			||||||
 | 
					            if (value < 19999999999999999)
 | 
				
			||||||
 | 
					                return ConvertFromMicroseconds(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return ConvertFromNanoseconds(value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -120,7 +125,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))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
 | 
					                    LibraryHelpers.StaticLogger?.LogWarning("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);
 | 
				
			||||||
@ -133,7 +138,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))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
 | 
					                    LibraryHelpers.StaticLogger?.LogWarning("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);
 | 
				
			||||||
@ -146,25 +151,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))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
 | 
					                    LibraryHelpers.StaticLogger?.LogWarning("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 (double.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue))
 | 
					            if (decimal.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var decimalValue))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                // Parse 1637745563.000 format
 | 
					                // Parse 1637745563.000 format
 | 
				
			||||||
                if (doubleValue <= 0)
 | 
					                if (decimalValue <= 0)
 | 
				
			||||||
                    return default;
 | 
					                    return default;
 | 
				
			||||||
                if (doubleValue < 19999999999)
 | 
					                if (decimalValue < 19999999999)
 | 
				
			||||||
                    return ConvertFromSeconds(doubleValue);
 | 
					                    return ConvertFromSeconds(decimalValue);
 | 
				
			||||||
                if (doubleValue < 19999999999999)
 | 
					                if (decimalValue < 19999999999999)
 | 
				
			||||||
                    return ConvertFromMilliseconds((long)doubleValue);
 | 
					                    return ConvertFromMilliseconds(decimalValue);
 | 
				
			||||||
                if (doubleValue < 19999999999999999)
 | 
					                if (decimalValue < 19999999999999999)
 | 
				
			||||||
                    return ConvertFromMicroseconds((long)doubleValue);
 | 
					                    return ConvertFromMicroseconds(decimalValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return ConvertFromNanoseconds((long)doubleValue);
 | 
					                return ConvertFromNanoseconds(decimalValue);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stringValue.Length == 10)
 | 
					            if (stringValue.Length == 10)
 | 
				
			||||||
@ -175,7 +180,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))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
 | 
					                    LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue);
 | 
				
			||||||
                    return default;
 | 
					                    return default;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -188,54 +193,70 @@ 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>
 | 
				
			||||||
        /// <param name="seconds"></param>
 | 
					        public static DateTime ConvertFromSeconds(decimal seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond));
 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static DateTime ConvertFromSeconds(double seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond));
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Convert a milliseconds since epoch (01-01-1970) value to DateTime
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="milliseconds"></param>
 | 
					 | 
				
			||||||
        /// <returns></returns>
 | 
					 | 
				
			||||||
        public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond));
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Convert a microseconds since epoch (01-01-1970) value to DateTime
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <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>
 | 
				
			||||||
        /// <param name="nanoseconds"></param>
 | 
					        public static DateTime ConvertFromSeconds(double seconds) => ConvertFromSeconds((decimal)seconds);
 | 
				
			||||||
        /// <returns></returns>
 | 
					        /// <summary>
 | 
				
			||||||
        public static DateTime ConvertFromNanoseconds(double nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * _ticksPerNanosecond));
 | 
					        /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static DateTime ConvertFromSeconds(long seconds) => ConvertFromSeconds((decimal)seconds);
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Convert a milliseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static DateTime ConvertFromMilliseconds(decimal milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond));
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </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>
 | 
				
			||||||
 | 
					        /// Convert a microseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static DateTime ConvertFromMicroseconds(decimal microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * _ticksPerMicrosecond));
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public static DateTime ConvertFromMicroseconds(double microseconds) => ConvertFromMicroseconds((decimal)microseconds);
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
 | 
				
			||||||
 | 
					        /// </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,4 +1,6 @@
 | 
				
			|||||||
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;
 | 
				
			||||||
@ -79,7 +81,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, out var warn);
 | 
					                return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
 | 
					            public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
 | 
				
			||||||
@ -98,20 +100,13 @@ 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, out var warn);
 | 
					            var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString);
 | 
				
			||||||
            if (t == null)
 | 
					            if (t == null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (warn)
 | 
					                if (isEmptyString && !_unknownValuesWarned.Contains(null))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (isEmptyString)
 | 
					                    // We received an empty string and have no mapping for it, and the property isn't nullable
 | 
				
			||||||
                    {
 | 
					                    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
 | 
				
			||||||
@ -122,10 +117,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn)
 | 
					        private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            isEmptyString = false;
 | 
					            isEmptyString = false;
 | 
				
			||||||
            warn = false;
 | 
					 | 
				
			||||||
            var enumType = typeof(T);
 | 
					            var enumType = typeof(T);
 | 
				
			||||||
            if (_mapping == null)
 | 
					            if (_mapping == null)
 | 
				
			||||||
                _mapping = AddMapping();
 | 
					                _mapping = AddMapping();
 | 
				
			||||||
@ -154,9 +148,8 @@ 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!);
 | 
				
			||||||
                        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");
 | 
					                        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");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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.8.0</PackageVersion>
 | 
					    <PackageVersion>9.12.0</PackageVersion>
 | 
				
			||||||
    <AssemblyVersion>9.8.0</AssemblyVersion>
 | 
					    <AssemblyVersion>9.12.0</AssemblyVersion>
 | 
				
			||||||
    <FileVersion>9.8.0</FileVersion>
 | 
					    <FileVersion>9.12.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.6" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" />
 | 
				
			||||||
    <PackageReference Include="System.Text.Json" Version="9.0.6" />
 | 
					    <PackageReference Include="System.Text.Json" Version="9.0.10" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup Label="Transitive Client Packages">
 | 
					  <ItemGroup Label="Transitive Client Packages">
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
@ -416,6 +416,36 @@ 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>
 | 
				
			||||||
@ -445,6 +475,8 @@ 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,4 +1,5 @@
 | 
				
			|||||||
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;
 | 
				
			||||||
@ -12,11 +13,50 @@ 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>
 | 
				
			||||||
@ -47,6 +87,22 @@ 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,7 +13,6 @@ 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;
 | 
				
			||||||
@ -72,11 +71,6 @@ 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"),
 | 
				
			||||||
@ -234,11 +228,6 @@ 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,6 +9,10 @@ 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>
 | 
				
			||||||
@ -21,10 +25,26 @@ namespace CryptoExchange.Net.Objects
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// ctor
 | 
					        /// ctor
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public AssetAlias(string exchangeName, string commonName)
 | 
					        public AssetAlias(string exchangeName, string commonName, AliasType type = AliasType.BothWays)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            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,12 +23,22 @@ 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.SingleOrDefault(x => x.CommonAssetName == commonName)?.ExchangeAssetName ?? commonName;
 | 
					        public string CommonToExchangeName(string commonName) => !AutoConvertEnabled ? commonName : Aliases.FirstOrDefault(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) => !AutoConvertEnabled ? exchangeName : Aliases.SingleOrDefault(x => x.ExchangeAssetName == exchangeName)?.CommonAssetName ?? exchangeName;
 | 
					        public string ExchangeToCommonName(string 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,4 +267,31 @@ 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,6 +15,7 @@ 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>>();
 | 
				
			||||||
@ -22,6 +23,11 @@ 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>
 | 
				
			||||||
@ -113,21 +119,34 @@ namespace CryptoExchange.Net.Objects.Sockets
 | 
				
			|||||||
            _connection.ActivityUnpaused += HandleUnpausedEvent;
 | 
					            _connection.ActivityUnpaused += HandleUnpausedEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _listener = subscription;
 | 
					            _listener = subscription;
 | 
				
			||||||
            _listener.Unsubscribed += HandleUnsubscribed;
 | 
					            _listener.StatusChanged += (x) => SubscriptionStatusChanged?.Invoke(x);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void HandleUnsubscribed()
 | 
					        private void UnsubscribeConnectionEvents()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _connection.ConnectionClosed -= HandleConnectionClosedEvent;
 | 
					            lock (_eventLock)
 | 
				
			||||||
            _connection.ConnectionLost -= HandleConnectionLostEvent;
 | 
					            {
 | 
				
			||||||
            _connection.ConnectionRestored -= HandleConnectionRestoredEvent;
 | 
					                if (!_connectionEventsSubscribed)
 | 
				
			||||||
            _connection.ResubscribingFailed -= HandleResubscribeFailedEvent;
 | 
					                    return;
 | 
				
			||||||
            _connection.ActivityPaused -= HandlePausedEvent;
 | 
					
 | 
				
			||||||
            _connection.ActivityUnpaused -= HandleUnpausedEvent;
 | 
					                _connection.ConnectionClosed -= HandleConnectionClosedEvent;
 | 
				
			||||||
 | 
					                _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();
 | 
				
			||||||
@ -138,6 +157,12 @@ 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();
 | 
				
			||||||
@ -148,6 +173,12 @@ 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();
 | 
				
			||||||
@ -158,6 +189,12 @@ 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();
 | 
				
			||||||
@ -168,6 +205,12 @@ 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();
 | 
				
			||||||
@ -178,6 +221,12 @@ 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();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										63
									
								
								CryptoExchange.Net/SharedApis/Enums/SharedAccountType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								CryptoExchange.Net/SharedApis/Enums/SharedAccountType.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					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>
 | 
				
			||||||
        EndpointOptions<GetBalancesRequest> GetBalancesOptions { get; }
 | 
					        GetBalancesOptions GetBalancesOptions { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Get balances for the user
 | 
					        /// Get balances for the user
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					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,18 +8,28 @@ namespace CryptoExchange.Net.SharedApis
 | 
				
			|||||||
    public record GetBalancesRequest : SharedRequest
 | 
					    public record GetBalancesRequest : SharedRequest
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Trading mode
 | 
					        /// Account type
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public TradingMode? TradingMode { get; set; }
 | 
					        public SharedAccountType? AccountType { 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 = null, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
 | 
					        public GetBalancesRequest(TradingMode tradingMode, ExchangeParameters? exchangeParameters = null) : base(exchangeParameters)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            TradingMode = tradingMode;
 | 
					            AccountType = tradingMode.ToAccountType();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										61
									
								
								CryptoExchange.Net/SharedApis/Models/Rest/TransferRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								CryptoExchange.Net/SharedApis/Models/Rest/TransferRequest.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					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,6 +17,7 @@
 | 
				
			|||||||
        /// 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
 | 
					    public record SharedFuturesKline : SharedSymbolModel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Open time
 | 
					        /// Open time
 | 
				
			||||||
@ -31,7 +31,8 @@ namespace CryptoExchange.Net.SharedApis
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// ctor
 | 
					        /// ctor
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public SharedFuturesKline(DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice)
 | 
					        public SharedFuturesKline(SharedSymbol? sharedSymbol, string symbol, 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
 | 
					    public record SharedKline : SharedSymbolModel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Open time
 | 
					        /// Open time
 | 
				
			||||||
@ -35,7 +35,8 @@ namespace CryptoExchange.Net.SharedApis
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// ctor
 | 
					        /// ctor
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public SharedKline(DateTime openTime, decimal closePrice, decimal highPrice, decimal lowPrice, decimal openPrice, decimal volume)
 | 
					        public SharedKline(SharedSymbol? sharedSymbol, string symbol, 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
 | 
					    public record SharedTrade : SharedSymbolModel
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /// <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(decimal quantity, decimal price, DateTime timestamp)
 | 
					        public SharedTrade(SharedSymbol? sharedSymbol, string symbol, decimal quantity, decimal price, DateTime timestamp) : base(sharedSymbol, symbol)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Quantity = quantity;
 | 
					            Quantity = quantity;
 | 
				
			||||||
            Price = price;
 | 
					            Price = price;
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,10 @@ 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,6 +33,11 @@ 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))
 | 
					                foreach (var subscription in _listeners.OfType<Subscription>().Where(l => l.UserSubscription && !l.IsClosingConnection))
 | 
				
			||||||
                    subscription.Reset();
 | 
					                    subscription.Reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                foreach (var query in _listeners.OfType<Query>().ToList())
 | 
					                foreach (var query in _listeners.OfType<Query>().ToList())
 | 
				
			||||||
@ -449,10 +449,7 @@ 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;
 | 
				
			||||||
@ -527,10 +524,10 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
                            continue;
 | 
					                            continue;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (processor is Subscription subscriptionProcessor && !subscriptionProcessor.Confirmed)
 | 
					                        if (processor is Subscription subscriptionProcessor && subscriptionProcessor.Status == SubscriptionStatus.Subscribing)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            // 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.Confirmed = true;
 | 
					                            subscriptionProcessor.Status = SubscriptionStatus.Subscribed;
 | 
				
			||||||
                            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
 | 
				
			||||||
@ -657,13 +654,16 @@ 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.IsResubscribing)
 | 
					            while (subscription.Status == SubscriptionStatus.Subscribing)
 | 
				
			||||||
                await Task.Delay(50).ConfigureAwait(false);
 | 
					                await Task.Delay(50).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            subscription.Closed = true;
 | 
					            subscription.Status = SubscriptionStatus.Closing;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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.Closed) && !DedicatedRequestConnection.IsDedicatedRequestConnection;
 | 
					                shouldCloseConnection = _listeners.OfType<Subscription>().All(r => !r.UserSubscription || r.Status == SubscriptionStatus.Closing || r.Status == SubscriptionStatus.Closed) && !DedicatedRequestConnection.IsDedicatedRequestConnection;
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (!anyDuplicateSubscription)
 | 
					            if (!anyDuplicateSubscription)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -693,6 +693,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (Status == SocketStatus.Closing)
 | 
					            if (Status == SocketStatus.Closing)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                subscription.Status = SubscriptionStatus.Closed;
 | 
				
			||||||
                _logger.AlreadyClosing(SocketId);
 | 
					                _logger.AlreadyClosing(SocketId);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -700,6 +701,7 @@ 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);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -707,7 +709,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
            lock (_listenersLock)
 | 
					            lock (_listenersLock)
 | 
				
			||||||
                _listeners.Remove(subscription);
 | 
					                _listeners.Remove(subscription);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            subscription.InvokeUnsubscribedHandler();
 | 
					            subscription.Status = SubscriptionStatus.Closed;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -991,7 +993,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                List<Subscription> subList;
 | 
					                List<Subscription> subList;
 | 
				
			||||||
                lock (_listenersLock)
 | 
					                lock (_listenersLock)
 | 
				
			||||||
                    subList = _listeners.OfType<Subscription>().Where(x => !x.Closed).Skip(batch * batchSize).Take(batchSize).ToList();
 | 
					                    subList = _listeners.OfType<Subscription>().Where(x => x.Active).Skip(batch * batchSize).Take(batchSize).ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (subList.Count == 0)
 | 
					                if (subList.Count == 0)
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
@ -1000,34 +1002,32 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
                foreach (var subscription in subList)
 | 
					                foreach (var subscription in subList)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    subscription.ConnectionInvocations = 0;
 | 
					                    subscription.ConnectionInvocations = 0;
 | 
				
			||||||
                    if (subscription.Closed)
 | 
					                    if (!subscription.Active)
 | 
				
			||||||
                        // Can be closed during resubscribing
 | 
					                        // Can be closed during resubscribing
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    subscription.IsResubscribing = true;
 | 
					                    subscription.Status = SubscriptionStatus.Subscribing;
 | 
				
			||||||
                    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.IsResubscribing = false;
 | 
					                        subscription.Status = SubscriptionStatus.Pending;
 | 
				
			||||||
                        return result;
 | 
					                        return result;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var subQuery = subscription.CreateSubscriptionQuery(this);
 | 
					                    var subQuery = subscription.CreateSubscriptionQuery(this);
 | 
				
			||||||
                    if (subQuery == null)
 | 
					                    if (subQuery == null)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        subscription.IsResubscribing = false;
 | 
					                        subscription.Status = SubscriptionStatus.Subscribed;
 | 
				
			||||||
                        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.IsResubscribing = false;
 | 
					                        subscription.Status = r.Result.Success ? SubscriptionStatus.Subscribed: SubscriptionStatus.Pending;
 | 
				
			||||||
                        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,20 +35,32 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public bool UserSubscription { get; set; }
 | 
					        public bool UserSubscription { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private SubscriptionStatus _status;
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Has the subscription been confirmed
 | 
					        /// Current subscription status
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public bool Confirmed { get; set; }
 | 
					        public SubscriptionStatus Status
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get => _status;
 | 
				
			||||||
 | 
					            set 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (_status == value)
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _status = value;
 | 
				
			||||||
 | 
					                Task.Run(() => StatusChanged?.Invoke(value));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Is the subscription closed
 | 
					        /// Whether the subscription is active
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public bool Closed { get; set; }
 | 
					        public bool Active => Status != SubscriptionStatus.Closing && Status != SubscriptionStatus.Closed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Is the subscription currently resubscribing
 | 
					        /// Whether the unsubscribing of this subscription lead to the closing of the connection
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public bool IsResubscribing { get; set; }
 | 
					        public bool IsClosingConnection { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Logger
 | 
					        /// Logger
 | 
				
			||||||
@ -77,7 +89,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Listener unsubscribed event
 | 
					        /// Listener unsubscribed event
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public event Action? Unsubscribed;
 | 
					        public event Action<SubscriptionStatus>? StatusChanged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Subscription topic
 | 
					        /// Subscription topic
 | 
				
			||||||
@ -167,7 +179,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        public void Reset()
 | 
					        public void Reset()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Confirmed = false;
 | 
					            Status = SubscriptionStatus.Pending;
 | 
				
			||||||
            DoHandleReset();
 | 
					            DoHandleReset();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -185,24 +197,16 @@ 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="Confirmed">True when the subscription query is handled (either accepted or rejected)</param>
 | 
					        /// <param name="Status">Subscription status</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,
 | 
				
			||||||
            bool Confirmed,
 | 
					            SubscriptionStatus Status,
 | 
				
			||||||
            int Invocations,
 | 
					            int Invocations,
 | 
				
			||||||
            MessageMatcher ListenMatcher
 | 
					            MessageMatcher ListenMatcher
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -213,7 +217,7 @@ namespace CryptoExchange.Net.Sockets
 | 
				
			|||||||
        /// <returns></returns>
 | 
					        /// <returns></returns>
 | 
				
			||||||
        public SubscriptionState GetState()
 | 
					        public SubscriptionState GetState()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new SubscriptionState(Id, Confirmed, TotalInvocations, MessageMatcher);
 | 
					            return new SubscriptionState(Id, Status, 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)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Confirmed = true;
 | 
					            Status = SubscriptionStatus.Subscribed;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <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.ParseFromDouble((double)value))
 | 
					                    if (time != DateTimeConverter.ParseFromDecimal(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,9 +325,12 @@ 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));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _snapshotId = data.Max(d => d.Timestamp.Ticks);
 | 
					                if (items.Any())
 | 
				
			||||||
                foreach (var item in items.OrderBy(d => d.Timestamp))
 | 
					                {
 | 
				
			||||||
                    _data.Add(item);
 | 
					                    _snapshotId = data.Max(d => d.Timestamp.Ticks);
 | 
				
			||||||
 | 
					                    foreach (var item in items.OrderBy(d => d.Timestamp))
 | 
				
			||||||
 | 
					                        _data.Add(item);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _snapshotSet = true;
 | 
					                _snapshotSet = true;
 | 
				
			||||||
                _changed = true;
 | 
					                _changed = true;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,30 +5,32 @@
 | 
				
			|||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <PackageReference Include="Binance.Net" Version="11.7.1" />
 | 
					    <PackageReference Include="Binance.Net" Version="11.10.0" />
 | 
				
			||||||
    <PackageReference Include="Bitfinex.Net" Version="9.7.0" />
 | 
					    <PackageReference Include="Bitfinex.Net" Version="9.10.0" />
 | 
				
			||||||
    <PackageReference Include="BitMart.Net" Version="2.8.0" />
 | 
					    <PackageReference Include="BitMart.Net" Version="2.11.0" />
 | 
				
			||||||
    <PackageReference Include="BloFin.Net" Version="1.0.0" />
 | 
					    <PackageReference Include="BloFin.Net" Version="1.3.0" />
 | 
				
			||||||
    <PackageReference Include="Bybit.Net" Version="5.8.0" />
 | 
					    <PackageReference Include="Bybit.Net" Version="5.12.0" />
 | 
				
			||||||
    <PackageReference Include="CoinEx.Net" Version="9.7.0" />
 | 
					    <PackageReference Include="CoinEx.Net" Version="9.10.0" />
 | 
				
			||||||
    <PackageReference Include="CoinW.Net" Version="1.4.0" />
 | 
					    <PackageReference Include="CoinW.Net" Version="1.7.0" />
 | 
				
			||||||
    <PackageReference Include="CryptoCom.Net" Version="2.8.0" />
 | 
					    <PackageReference Include="CryptoCom.Net" Version="2.11.0" />
 | 
				
			||||||
    <PackageReference Include="DeepCoin.Net" Version="2.7.0" />
 | 
					    <PackageReference Include="DeepCoin.Net" Version="2.10.0" />
 | 
				
			||||||
    <PackageReference Include="GateIo.Net" Version="2.8.1" />
 | 
					    <PackageReference Include="GateIo.Net" Version="2.12.0" />
 | 
				
			||||||
    <PackageReference Include="HyperLiquid.Net" Version="2.12.0" />
 | 
					    <PackageReference Include="HyperLiquid.Net" Version="2.16.0" />
 | 
				
			||||||
    <PackageReference Include="JK.BingX.Net" Version="2.7.0" />
 | 
					    <PackageReference Include="JK.BingX.Net" Version="2.10.0" />
 | 
				
			||||||
    <PackageReference Include="JK.Bitget.Net" Version="2.7.1" />
 | 
					    <PackageReference Include="JK.Bitget.Net" Version="2.11.0" />
 | 
				
			||||||
    <PackageReference Include="JK.Mexc.Net" Version="3.8.0" />
 | 
					    <PackageReference Include="JK.Mexc.Net" Version="3.11.0" />
 | 
				
			||||||
    <PackageReference Include="JK.OKX.Net" Version="3.7.1" />
 | 
					    <PackageReference Include="JK.OKX.Net" Version="3.10.0" />
 | 
				
			||||||
    <PackageReference Include="JKorf.BitMEX.Net" Version="2.7.0" />
 | 
					    <PackageReference Include="Jkorf.Aster.Net" Version="1.2.0" />
 | 
				
			||||||
    <PackageReference Include="JKorf.Coinbase.Net" Version="2.7.0" />
 | 
					    <PackageReference Include="JKorf.BitMEX.Net" Version="2.10.0" />
 | 
				
			||||||
    <PackageReference Include="JKorf.HTX.Net" Version="7.7.0" />
 | 
					    <PackageReference Include="JKorf.Coinbase.Net" Version="2.10.0" />
 | 
				
			||||||
    <PackageReference Include="KrakenExchange.Net" Version="6.7.0" />
 | 
					    <PackageReference Include="JKorf.HTX.Net" Version="7.10.0" />
 | 
				
			||||||
    <PackageReference Include="Kucoin.Net" Version="7.7.1" />
 | 
					    <PackageReference Include="JKorf.Upbit.Net" Version="1.1.0" />
 | 
				
			||||||
 | 
					    <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.6.0" />
 | 
					    <PackageReference Include="Toobit.Net" Version="1.9.0" />
 | 
				
			||||||
    <PackageReference Include="WhiteBit.Net" Version="2.8.0" />
 | 
					    <PackageReference Include="WhiteBit.Net" Version="2.11.0" />
 | 
				
			||||||
    <PackageReference Include="XT.Net" Version="2.7.0" />
 | 
					    <PackageReference Include="XT.Net" Version="2.10.0" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
@page "/"
 | 
					@page "/"
 | 
				
			||||||
 | 
					@inject IAsterRestClient asterClient
 | 
				
			||||||
@inject IBinanceRestClient binanceClient
 | 
					@inject IBinanceRestClient binanceClient
 | 
				
			||||||
@inject IBingXRestClient bingXClient
 | 
					@inject IBingXRestClient bingXClient
 | 
				
			||||||
@inject IBitfinexRestClient bitfinexClient
 | 
					@inject IBitfinexRestClient bitfinexClient
 | 
				
			||||||
@ -20,6 +21,7 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,6 +36,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    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");
 | 
				
			||||||
@ -49,16 +52,20 @@
 | 
				
			|||||||
        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(); // HyperLiquid does not have BTC spot trading
 | 
					        var hyperLiquidTask = hyperLiquidClient.FuturesApi.ExchangeData.GetExchangeInfoAndTickersAsync();
 | 
				
			||||||
        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(binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bybitTask, coinexTask, deepCoinTask, gateioTask, htxTask, krakenTask, kucoinTask, mexcTask, okxTask);
 | 
					        await Task.WhenAll(asterTask, binanceTask, bingXTask, bitfinexTask, bitgetTask, bitmartTask, bloFinTask, bitmexTask, bybitTask, coinexTask, coinWTask, 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);
 | 
				
			||||||
@ -131,6 +138,9 @@
 | 
				
			|||||||
        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,4 +1,5 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -20,6 +21,7 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -43,6 +45,8 @@
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        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)),
 | 
				
			||||||
@ -69,6 +73,7 @@
 | 
				
			|||||||
            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,6 +1,7 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -24,9 +25,11 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -48,6 +51,7 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -83,6 +87,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        _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") },
 | 
				
			||||||
@ -104,6 +109,7 @@
 | 
				
			|||||||
                { "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,6 +1,7 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -25,9 +26,11 @@
 | 
				
			|||||||
@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
 | 
				
			||||||
@ -49,11 +52,12 @@
 | 
				
			|||||||
@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>ETH-BTC trade Trackers, live updates:</h3>
 | 
					<h3>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))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -73,38 +77,39 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    protected override async Task OnInitializedAsync()
 | 
					    protected override async Task OnInitializedAsync()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var usdcSpotSymbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDC");
 | 
					        var symbol = new SharedSymbol(TradingMode.Spot, "BTC", SharedSymbol.UsdOrStable);
 | 
				
			||||||
        var usdtSpotSymbol = new SharedSymbol(TradingMode.Spot, "BTC", "USDT");
 | 
					        var futuresSymbol = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", SharedSymbol.UsdOrStable);
 | 
				
			||||||
        var usdtPerpLinSymbol = new SharedSymbol(TradingMode.PerpetualLinear, "BTC", "USDT");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _trackers = new List<ITradeTracker>
 | 
					        _trackers = new List<ITradeTracker>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                { binanceFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { asterFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bingXFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { binanceFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bitfinexFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bingXFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bitgetFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bitfinexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bitmartFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bitgetFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bitmexFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bitmartFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bloFinFactory.CreateTradeTracker(usdtPerpLinSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bitmexFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { bybitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bloFinFactory.CreateTradeTracker(futuresSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { coinbaseFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { bybitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { coinExFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { coinbaseFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { coinWFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { coinExFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { cryptocomFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { coinWFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { deepCoinFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { cryptocomFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { gateioFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { deepCoinFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { htxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { gateioFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { hyperLiquidFactory.CreateTradeTracker(usdcSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { htxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { krakenFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { hyperLiquidFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { kucoinFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { krakenFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { mexcFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { kucoinFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { okxFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { mexcFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { toobitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { okxFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { whitebitFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { toobitFactory.CreateTradeTracker(symbol, period: TimeSpan.FromMinutes(5)) },
 | 
				
			||||||
                { xtFactory.CreateTradeTracker(usdtSpotSymbol, period: TimeSpan.FromMinutes(5)) },
 | 
					                { upbitFactory.CreateTradeTracker(symbol, 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()));
 | 
					        await Task.WhenAll(_trackers.Select(b => b.StartAsync(false)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 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,6 +33,7 @@ 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();
 | 
				
			||||||
@ -53,6 +54,7 @@ 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,6 +8,7 @@
 | 
				
			|||||||
@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;
 | 
				
			||||||
@ -28,6 +29,7 @@
 | 
				
			|||||||
@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.1.0" />
 | 
							<PackageReference Include="Binance.Net" Version="11.10.0" />
 | 
				
			||||||
	<PackageReference Include="Bitfinex.Net" Version="9.1.0" />
 | 
						<PackageReference Include="Bitfinex.Net" Version="9.10.0" />
 | 
				
			||||||
	<PackageReference Include="BitMart.Net" Version="2.1.0" />
 | 
						<PackageReference Include="BitMart.Net" Version="2.11.0" />
 | 
				
			||||||
	<PackageReference Include="Bybit.Net" Version="5.1.0" />
 | 
						<PackageReference Include="Bybit.Net" Version="5.12.0" />
 | 
				
			||||||
	<PackageReference Include="CoinEx.Net" Version="9.1.0" />
 | 
						<PackageReference Include="CoinEx.Net" Version="9.10.0" />
 | 
				
			||||||
	<PackageReference Include="CryptoCom.Net" Version="2.1.0" />
 | 
						<PackageReference Include="CryptoCom.Net" Version="2.11.0" />
 | 
				
			||||||
	<PackageReference Include="GateIo.Net" Version="2.1.0" />
 | 
						<PackageReference Include="GateIo.Net" Version="2.12.0" />
 | 
				
			||||||
	<PackageReference Include="JK.Bitget.Net" Version="2.1.0" />
 | 
						<PackageReference Include="JK.Bitget.Net" Version="2.11.0" />
 | 
				
			||||||
	<PackageReference Include="JK.Mexc.Net" Version="3.1.0" />
 | 
						<PackageReference Include="JK.Mexc.Net" Version="3.11.0" />
 | 
				
			||||||
	<PackageReference Include="JK.OKX.Net" Version="3.1.0" />
 | 
						<PackageReference Include="JK.OKX.Net" Version="3.10.0" />
 | 
				
			||||||
	<PackageReference Include="JKorf.Coinbase.Net" Version="2.1.0" />
 | 
						<PackageReference Include="JKorf.Coinbase.Net" Version="2.10.0" />
 | 
				
			||||||
	<PackageReference Include="JKorf.HTX.Net" Version="7.1.0" />
 | 
						<PackageReference Include="JKorf.HTX.Net" Version="7.10.0" />
 | 
				
			||||||
	<PackageReference Include="KrakenExchange.Net" Version="6.1.0" />
 | 
						<PackageReference Include="KrakenExchange.Net" Version="6.10.0" />
 | 
				
			||||||
		<PackageReference Include="Kucoin.Net" Version="7.1.0" />
 | 
							<PackageReference Include="Kucoin.Net" Version="7.10.0" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 | 
				
			|||||||
@ -8,9 +8,9 @@
 | 
				
			|||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <PackageReference Include="Binance.Net" Version="11.1.0" />
 | 
					    <PackageReference Include="Binance.Net" Version="11.10.0" />
 | 
				
			||||||
    <PackageReference Include="BitMart.Net" Version="2.1.0" />
 | 
					    <PackageReference Include="BitMart.Net" Version="2.11.0" />
 | 
				
			||||||
    <PackageReference Include="JK.OKX.Net" Version="3.1.0" />
 | 
					    <PackageReference Include="JK.OKX.Net" Version="3.10.0" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
								
							@ -12,6 +12,7 @@ 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)|-|-|
 | 
				
			||||||
@ -34,6 +35,7 @@ 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%|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -64,6 +66,28 @@ 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