1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-13 09:23:04 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
JKorf 73764970b0 . 2025-08-25 19:11:50 +02:00
JKorf 9ba29035b2 Merge branch 'master' into feature/code-analyzis 2025-08-25 17:37:46 +02:00
JKorf 4c953e2c87 wip 2025-08-24 21:58:52 +02:00
383 changed files with 23512 additions and 24992 deletions
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net.Protobuf</PackageId> <PackageId>CryptoExchange.Net.Protobuf</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>Protobuf support for CryptoExchange.Net</Description> <Description>Protobuf support for CryptoExchange.Net</Description>
<PackageVersion>9.12.0</PackageVersion> <PackageVersion>9.6.0</PackageVersion>
<AssemblyVersion>9.12.0</AssemblyVersion> <AssemblyVersion>9.6.0</AssemblyVersion>
<FileVersion>9.12.0</FileVersion> <FileVersion>9.6.0</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags> <PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@@ -41,7 +41,7 @@
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile> <DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CryptoExchange.Net" Version="9.12.0" /> <PackageReference Include="CryptoExchange.Net" Version="9.6.0" />
<PackageReference Include="protobuf-net" Version="3.2.56" /> <PackageReference Include="protobuf-net" Version="3.2.56" />
</ItemGroup> </ItemGroup>
</Project> </Project>
-21
View File
@@ -5,27 +5,6 @@
Protobuf support for CryptoExchange.Net. Protobuf support for CryptoExchange.Net.
## Release notes ## Release notes
* Version 9.12.0 - 03 Nov 2025
* Updated CryptoExchange.Net version to 9.12.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.11.1 - 30 Oct 2025
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.11.0 - 30 Oct 2025
* Updated CryptoExchange.Net version to 9.11.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.10.0 - 15 Oct 2025
* Updated CryptoExchange.Net version to 9.10.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.9.0 - 06 Oct 2025
* Updated CryptoExchange.Net version to 9.9.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.8.0 - 30 Sep 2025
* Updated CryptoExchange.Net version to 9.8.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.7.0 - 01 Sep 2025
* Updated CryptoExchange.Net version to 9.7.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.6.0 - 25 Aug 2025 * Version 9.6.0 - 25 Aug 2025
* Updated CryptoExchange.Net version to 9.6.0 * Updated CryptoExchange.Net version to 9.6.0
@@ -5,7 +5,6 @@ using NUnit.Framework.Legacy;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -114,7 +113,6 @@ namespace CryptoExchange.Net.UnitTests
{ {
var result = new WebCallResult<TestObjectResult>( var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK, System.Net.HttpStatusCode.OK,
HttpVersion.Version11,
new KeyValuePair<string, string[]>[0], new KeyValuePair<string, string[]>[0],
TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1),
null, null,
@@ -145,7 +143,6 @@ namespace CryptoExchange.Net.UnitTests
{ {
var result = new WebCallResult<TestObjectResult>( var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK, System.Net.HttpStatusCode.OK,
HttpVersion.Version11,
new KeyValuePair<string, string[]>[0], new KeyValuePair<string, string[]>[0],
TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1),
null, null,
@@ -6,10 +6,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0"></PackageReference> <None Include="..\CryptoExchange.Net\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"></PackageReference>
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.4.0"></PackageReference> <PackageReference Include="NUnit" Version="4.3.2"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0"></PackageReference> <PackageReference Include="NUnit3TestAdapter" Version="5.0.0"></PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -32,7 +32,6 @@ namespace CryptoExchange.Net.UnitTests
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.532, 0.532)] [TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.532, 0.532)]
[TestCase(0.1, 1, 0.0001, RoundingType.Down, 0.5516592, 0.5516)] [TestCase(0.1, 1, 0.0001, RoundingType.Down, 0.5516592, 0.5516)]
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.5516592, 0.5517)] [TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.5516592, 0.5517)]
[TestCase(0, 1, 0.000000001, RoundingType.Closest, 0.0000097232, 0.000009723)]
public void AdjustValueStepTests(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal input, decimal expected) public void AdjustValueStepTests(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal input, decimal expected)
{ {
var result = ExchangeHelpers.AdjustValueStep(min, max, step, roundingType, input); var result = ExchangeHelpers.AdjustValueStep(min, max, step, roundingType, input);
@@ -202,7 +202,7 @@ namespace CryptoExchange.Net.UnitTests
await sub; await sub;
// assert // assert
ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed); ClassicAssert.IsFalse(client.SubClient.TestSubscription.Confirmed);
} }
[TestCase()] [TestCase()]
@@ -225,7 +225,7 @@ namespace CryptoExchange.Net.UnitTests
await sub; await sub;
// assert // assert
Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed); Assert.That(client.SubClient.TestSubscription.Confirmed);
} }
} }
} }
@@ -60,8 +60,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray()); request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray());
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => .Callback<HttpMethod, Uri, int>((method, uri, id) =>
{ {
request.Setup(a => a.Uri).Returns(uri); request.Setup(a => a.Uri).Returns(uri);
request.Setup(a => a.Method).Returns(method); request.Setup(a => a.Method).Returns(method);
@@ -69,8 +69,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => .Callback<HttpMethod, Uri, int>((method, uri, id) =>
{ {
request.Setup(a => a.Uri).Returns(uri); request.Setup(a => a.Uri).Returns(uri);
request.Setup(a => a.Method).Returns(method); request.Setup(a => a.Method).Returns(method);
@@ -90,12 +90,12 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we); request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we);
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Returns(request.Object); .Returns(request.Object);
} }
@@ -118,13 +118,13 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetHeaders()).Returns(headers.ToArray()); request.Setup(c => c.GetHeaders()).Returns(headers.ToArray());
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri)) .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri)) .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
.Returns(request.Object); .Returns(request.Object);
} }
} }
@@ -1,132 +0,0 @@
//using System;
//using System.IO;
//using System.Net.WebSockets;
//using System.Security.Authentication;
//using System.Text;
//using System.Threading.Tasks;
//using CryptoExchange.Net.Interfaces;
//using CryptoExchange.Net.Objects;
//namespace CryptoExchange.Net.UnitTests.TestImplementations
//{
// public class TestSocket: IWebsocket
// {
// public bool CanConnect { get; set; }
// public bool Connected { get; set; }
// public event Func<Task> OnClose;
//#pragma warning disable 0067
// public event Func<Task> OnReconnected;
// public event Func<Task> OnReconnecting;
// public event Func<int, Task> OnRequestRateLimited;
//#pragma warning restore 0067
// public event Func<int, Task> OnRequestSent;
// public event Func<WebSocketMessageType, ReadOnlyMemory<byte>, Task> OnStreamMessage;
// public event Func<Exception, Task> OnError;
// public event Func<Task> OnOpen;
// public Func<Task<Uri>> GetReconnectionUrl { get; set; }
// public int Id { get; }
// public bool ShouldReconnect { get; set; }
// public TimeSpan Timeout { get; set; }
// public Func<string, string> DataInterpreterString { get; set; }
// public Func<byte[], string> DataInterpreterBytes { get; set; }
// public DateTime? DisconnectTime { get; set; }
// public string Url { get; }
// public bool IsClosed => !Connected;
// public bool IsOpen => Connected;
// public bool PingConnection { get; set; }
// public TimeSpan PingInterval { get; set; }
// public SslProtocols SSLProtocols { get; set; }
// public Encoding Encoding { get; set; }
// public int ConnectCalls { get; private set; }
// public bool Reconnecting { get; set; }
// public string Origin { get; set; }
// public int? RatelimitPerSecond { get; set; }
// public double IncomingKbps => throw new NotImplementedException();
// public Uri Uri => new Uri("");
// public TimeSpan KeepAliveInterval { get; set; }
// public static int lastId = 0;
// public static object lastIdLock = new object();
// public TestSocket()
// {
// lock (lastIdLock)
// {
// Id = lastId + 1;
// lastId++;
// }
// }
// public Task<CallResult> ConnectAsync()
// {
// Connected = CanConnect;
// ConnectCalls++;
// if (CanConnect)
// InvokeOpen();
// return Task.FromResult(CanConnect ? new CallResult(null) : new CallResult(new CantConnectError()));
// }
// public bool Send(int requestId, string data, int weight)
// {
// if(!Connected)
// throw new Exception("Socket not connected");
// OnRequestSent?.Invoke(requestId);
// return true;
// }
// public void Reset()
// {
// }
// public Task CloseAsync()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// OnClose?.Invoke();
// return Task.FromResult(0);
// }
// public void SetProxy(string host, int port)
// {
// throw new NotImplementedException();
// }
// public void Dispose()
// {
// }
// public void InvokeClose()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// Reconnecting = true;
// OnClose?.Invoke();
// }
// public void InvokeOpen()
// {
// OnOpen?.Invoke();
// }
// public void InvokeMessage(string data)
// {
// OnStreamMessage?.Invoke(WebSocketMessageType.Text, new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(data))).Wait();
// }
// public void SetProxy(ApiProxy proxy)
// {
// throw new NotImplementedException();
// }
// public void InvokeError(Exception error)
// {
// OnError?.Invoke(error);
// }
// public Task ReconnectAsync() => Task.CompletedTask;
// }
//}
+183
View File
@@ -0,0 +1,183 @@
root = true
[*]
# Indentation and spacing
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 140
insert_final_newline = true
# ReSharper code style properties
resharper_csharp_keep_existing_embedded_arrangement = false
resharper_csharp_place_accessorholder_attribute_on_same_line = false
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_csharp_blank_lines_around_single_line_auto_property = 1
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_trailing_comma_in_multiline_lists = true
[*.cs]
indent_size = 4
# Code style conventions
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_object_initializer = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_namespace_declarations = file_scoped:warning
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
csharp_prefer_braces = when_multiline:warning
# Analyzer preferences
dotnet_diagnostic.CA2007.severity = warning # Call ConfigureAwait on the awaited Task.
dotnet_code_quality.CA2007.exclude_async_void_methods = true
dotnet_code_quality.CA2007.output_kind = DynamicallyLinkedLibrary
dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types
dotnet_diagnostic.CA1051.severity = none # Do not declare visible instance fields
dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException throw helper
dotnet_diagnostic.CA1720.severity = none # Identifiers should not contain type names
dotnet_diagnostic.CA1716.severity = none # Identifiers should not match keywords
dotnet_diagnostic.CA1835.severity = none # Use ArgumentNullException throw helper
dotnet_diagnostic.CA1846.severity = none # Prefer AsSpan over Substring
dotnet_diagnostic.CA1848.severity = none # Use the LoggerMessage delegates
dotnet_diagnostic.CA1850.severity = none # Prefer static HashData method over ComputeHash
dotnet_diagnostic.CA1866.severity = none # Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char
dotnet_diagnostic.CA2201.severity = none # Do not raise reserved exception types
dotnet_diagnostic.CA2208.severity = none # Do not raise reserved exception types
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
[*.xml]
ij_xml_space_inside_empty_tag = true
[*.cs]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.style = fields_start_with__
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.fields_start_with__.required_prefix = _
dotnet_naming_style.fields_start_with__.required_suffix =
dotnet_naming_style.fields_start_with__.word_separator =
dotnet_naming_style.fields_start_with__.capitalization = camel_case
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:suggestion
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
[*.vb]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+4 -5
View File
@@ -1,6 +1,5 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CryptoExchange.Net.UnitTests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CryptoExchange.Net.UnitTests")]
namespace System.Runtime.CompilerServices namespace System.Runtime.CompilerServices;
{
internal static class IsExternalInit { } internal static class IsExternalInit { }
}
@@ -1,12 +1,11 @@
using System; using System;
namespace CryptoExchange.Net.Attributes namespace CryptoExchange.Net.Attributes;
/// <summary>
/// Used for conversion in ArrayConverter
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class JsonConversionAttribute: Attribute
{ {
/// <summary>
/// Used for conversion in ArrayConverter
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class JsonConversionAttribute: Attribute
{
}
} }
@@ -1,13 +1,13 @@
using System; using System;
namespace CryptoExchange.Net.Attributes namespace CryptoExchange.Net.Attributes;
/// <summary>
/// Map a enum entry to string values
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class MapAttribute : Attribute
{ {
/// <summary>
/// Map a enum entry to string values
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class MapAttribute : Attribute
{
/// <summary> /// <summary>
/// Values mapping to the enum entry /// Values mapping to the enum entry
/// </summary> /// </summary>
@@ -21,5 +21,4 @@ namespace CryptoExchange.Net.Attributes
{ {
Values = maps; Values = maps;
} }
}
} }
@@ -1,15 +1,12 @@
using System; using System;
using System.IO;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Converters.MessageParsing;
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Api credentials, used to sign requests accessing private endpoints
/// </summary>
public class ApiCredentials
{ {
/// <summary>
/// Api credentials, used to sign requests accessing private endpoints
/// </summary>
public class ApiCredentials
{
/// <summary> /// <summary>
/// The api key / label to authenticate requests /// The api key / label to authenticate requests
/// </summary> /// </summary>
@@ -56,5 +53,4 @@ namespace CryptoExchange.Net.Authentication
{ {
return new ApiCredentials(Key, Secret, Pass, CredentialType); return new ApiCredentials(Key, Secret, Pass, CredentialType);
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Credentials type
/// </summary>
public enum ApiCredentialsType
{ {
/// <summary>
/// Credentials type
/// </summary>
public enum ApiCredentialsType
{
/// <summary> /// <summary>
/// Hmac keys credentials /// Hmac keys credentials
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Rsa keys credentials in pem/base64 format. Only available for .NetStandard 2.1 and up, use xml format for lower. /// Rsa keys credentials in pem/base64 format. Only available for .NetStandard 2.1 and up, use xml format for lower.
/// </summary> /// </summary>
RsaPem RsaPem
}
} }
@@ -1,21 +1,20 @@
using CryptoExchange.Net.Clients; using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.SystemTextJson; using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Base class for authentication providers
/// </summary>
public abstract class AuthenticationProvider
{ {
/// <summary>
/// Base class for authentication providers
/// </summary>
public abstract class AuthenticationProvider
{
internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider(); internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider();
/// <summary> /// <summary>
@@ -209,7 +208,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static string SignMD5(string data, SignOutputType? outputType = null) protected static string SignMD5(string data, SignOutputType? outputType = null)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data)); var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
} }
@@ -222,7 +223,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static string SignMD5(byte[] data, SignOutputType? outputType = null) protected static string SignMD5(byte[] data, SignOutputType? outputType = null)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
var resultBytes = encryptor.ComputeHash(data); var resultBytes = encryptor.ComputeHash(data);
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
} }
@@ -234,7 +237,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static byte[] SignMD5Bytes(string data) protected static byte[] SignMD5Bytes(string data)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data)); return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
} }
@@ -454,13 +459,17 @@ namespace CryptoExchange.Net.Authentication
else else
return stringSerializer.Serialize(parameters); return stringSerializer.Serialize(parameters);
} }
} }
/// <inheritdoc />
public abstract class AuthenticationProvider<TApiCredentials> : AuthenticationProvider where TApiCredentials : ApiCredentials
{
/// <inheritdoc /> /// <inheritdoc />
public abstract class AuthenticationProvider<TApiCredentials> : AuthenticationProvider where TApiCredentials : ApiCredentials #pragma warning disable IDE1006 // Naming Styles
{ #pragma warning disable CA1707 // Naming Styles
/// <inheritdoc />
protected new TApiCredentials _credentials => (TApiCredentials)base._credentials; protected new TApiCredentials _credentials => (TApiCredentials)base._credentials;
#pragma warning restore IDE1006 // Naming Styles
#pragma warning restore CA1707 // Naming Styles
/// <summary> /// <summary>
/// ctor /// ctor
@@ -469,5 +478,4 @@ namespace CryptoExchange.Net.Authentication
protected AuthenticationProvider(TApiCredentials credentials) : base(credentials) protected AuthenticationProvider(TApiCredentials credentials) : base(credentials)
{ {
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Output string type
/// </summary>
public enum SignOutputType
{ {
/// <summary>
/// Output string type
/// </summary>
public enum SignOutputType
{
/// <summary> /// <summary>
/// Hex string /// Hex string
/// </summary> /// </summary>
@@ -13,5 +13,4 @@
/// Base64 string /// Base64 string
/// </summary> /// </summary>
Base64 Base64
}
} }
+4 -5
View File
@@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
namespace CryptoExchange.Net.Caching namespace CryptoExchange.Net.Caching;
internal class MemoryCache
{ {
internal class MemoryCache
{
private readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>(); private readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>();
private readonly object _lock = new object(); private readonly object _lock = new object();
@@ -49,5 +49,4 @@ namespace CryptoExchange.Net.Caching
Value = value; Value = value;
} }
} }
}
} }
+16 -10
View File
@@ -1,20 +1,18 @@
using System; using System;
using System.Collections.Generic;
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base API for all API clients
/// </summary>
public abstract class BaseApiClient : IDisposable, IBaseApiClient
{ {
/// <summary>
/// Base API for all API clients
/// </summary>
public abstract class BaseApiClient : IDisposable, IBaseApiClient
{
/// <summary> /// <summary>
/// Logger /// Logger
/// </summary> /// </summary>
@@ -126,9 +124,17 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public virtual void Dispose() public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public virtual void Dispose(bool disposing)
{ {
_disposing = true; _disposing = true;
} }
}
} }
+24 -10
View File
@@ -1,17 +1,16 @@
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// The base for all clients, websocket client and rest client
/// </summary>
public abstract class BaseClient : IDisposable
{ {
/// <summary>
/// The base for all clients, websocket client and rest client
/// </summary>
public abstract class BaseClient : IDisposable
{
/// <summary> /// <summary>
/// Version of the CryptoExchange.Net base library /// Version of the CryptoExchange.Net base library
/// </summary> /// </summary>
@@ -80,7 +79,7 @@ namespace CryptoExchange.Net.Clients
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
ClientOptions = options; ClientOptions = options;
_logger.Log(LogLevel.Trace, $"Client configuration: {options}, CryptoExchange.Net: v{CryptoExchangeLibVersion}, {Exchange}.Net: v{ExchangeLibVersion}"); _logger.Log(LogLevel.Trace, "Client configuration: {Options}, CryptoExchange.Net: v{CryptoExchangeVersion}, {Exchange}.Net: v{ExchangeVersion}", options, CryptoExchangeLibVersion, Exchange, ExchangeLibVersion);
} }
/// <summary> /// <summary>
@@ -102,6 +101,7 @@ namespace CryptoExchange.Net.Clients
if (ClientOptions == null) if (ClientOptions == null)
throw new InvalidOperationException("Client should have called Initialize before adding API clients"); throw new InvalidOperationException("Client should have called Initialize before adding API clients");
_logger.Log(LogLevel.Trace, " {ApiClient}, base address: {BaseAddress}", apiClient.GetType().Name, apiClient.BaseAddress);
ApiClients.Add(apiClient); ApiClients.Add(apiClient);
return apiClient; return apiClient;
} }
@@ -119,10 +119,24 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public virtual void Dispose() public void Dispose()
{ {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public virtual void Dispose(bool disposing)
{
if (disposing)
{
_logger.Log(LogLevel.Debug, "Disposing client");
foreach (var client in ApiClients) foreach (var client in ApiClients)
client.Dispose(); client.Dispose();
} }
} }
} }
+6 -9
View File
@@ -3,13 +3,13 @@ using CryptoExchange.Net.Interfaces;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base rest client
/// </summary>
public abstract class BaseRestClient : BaseClient, IRestClient
{ {
/// <summary>
/// Base rest client
/// </summary>
public abstract class BaseRestClient : BaseClient, IRestClient
{
/// <inheritdoc /> /// <inheritdoc />
public int TotalRequestsMade => ApiClients.OfType<RestApiClient>().Sum(s => s.TotalRequestsMade); public int TotalRequestsMade => ApiClients.OfType<RestApiClient>().Sum(s => s.TotalRequestsMade);
@@ -21,8 +21,5 @@ 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("CryptoExchange");
}
} }
} }
+7 -11
View File
@@ -1,22 +1,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions; using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base for socket client implementations
/// </summary>
public abstract class BaseSocketClient : BaseClient, ISocketClient
{ {
/// <summary>
/// Base for socket client implementations
/// </summary>
public abstract class BaseSocketClient : BaseClient, ISocketClient
{
#region fields #region fields
/// <summary> /// <summary>
@@ -40,8 +39,6 @@ namespace CryptoExchange.Net.Clients
protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name) protected BaseSocketClient(ILoggerFactory? loggerFactory, string name) : base(loggerFactory, name)
{ {
_logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name); _logger = loggerFactory?.CreateLogger(name + ".SocketClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
LibraryHelpers.StaticLogger = loggerFactory?.CreateLogger("CryptoExchange");
} }
/// <summary> /// <summary>
@@ -130,5 +127,4 @@ namespace CryptoExchange.Net.Clients
return result; return result;
} }
}
} }
+19 -8
View File
@@ -1,14 +1,14 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base crypto client
/// </summary>
public class CryptoBaseClient : IDisposable
{ {
/// <summary>
/// Base crypto client
/// </summary>
public class CryptoBaseClient : IDisposable
{
private readonly Dictionary<Type, object> _serviceCache = new Dictionary<Type, object>(); private readonly Dictionary<Type, object> _serviceCache = new Dictionary<Type, object>();
/// <summary> /// <summary>
@@ -59,9 +59,20 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public void Dispose() public void Dispose(bool disposing)
{
if (disposing)
{ {
_serviceCache.Clear(); _serviceCache.Clear();
} }
} }
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
@@ -1,14 +1,11 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <inheritdoc />
public class CryptoRestClient : CryptoBaseClient, ICryptoRestClient
{ {
/// <inheritdoc />
public class CryptoRestClient : CryptoBaseClient, ICryptoRestClient
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -23,5 +20,4 @@ namespace CryptoExchange.Net.Clients
public CryptoRestClient(IServiceProvider serviceProvider) : base(serviceProvider) public CryptoRestClient(IServiceProvider serviceProvider) : base(serviceProvider)
{ {
} }
}
} }
@@ -1,11 +1,11 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using System; using System;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <inheritdoc />
public class CryptoSocketClient : CryptoBaseClient, ICryptoSocketClient
{ {
/// <inheritdoc />
public class CryptoSocketClient : CryptoBaseClient, ICryptoSocketClient
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -20,5 +20,4 @@ namespace CryptoExchange.Net.Clients
public CryptoSocketClient(IServiceProvider serviceProvider) : base(serviceProvider) public CryptoSocketClient(IServiceProvider serviceProvider) : base(serviceProvider)
{ {
} }
}
} }
+24 -44
View File
@@ -18,13 +18,13 @@ using CryptoExchange.Net.RateLimiting.Interfaces;
using CryptoExchange.Net.Requests; using CryptoExchange.Net.Requests;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base rest API client for interacting with a REST API
/// </summary>
public abstract class RestApiClient : BaseApiClient, IRestApiClient
{ {
/// <summary>
/// Base rest API client for interacting with a REST API
/// </summary>
public abstract class RestApiClient : BaseApiClient, IRestApiClient
{
/// <inheritdoc /> /// <inheritdoc />
public IRequestFactory RequestFactory { get; set; } = new RequestFactory(); public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
@@ -106,7 +106,7 @@ namespace CryptoExchange.Net.Clients
options, options,
apiOptions) apiOptions)
{ {
RequestFactory.Configure(options, httpClient); RequestFactory.Configure(options.Proxy, options.RequestTimeout, httpClient);
} }
/// <summary> /// <summary>
@@ -388,7 +388,7 @@ namespace CryptoExchange.Net.Clients
queryString = $"?{queryString}"; queryString = $"?{queryString}";
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString); var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
var request = RequestFactory.Create(ClientOptions.HttpVersion, definition.Method, uri, requestId); var request = RequestFactory.Create(definition.Method, uri, requestId);
request.Accept = Constants.JsonContentHeader; request.Accept = Constants.JsonContentHeader;
foreach (var header in requestConfiguration.Headers) foreach (var header in requestConfiguration.Headers)
@@ -443,6 +443,9 @@ namespace CryptoExchange.Net.Clients
{ {
response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false); response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
sw.Stop(); sw.Stop();
var statusCode = response.StatusCode;
var headers = response.ResponseHeaders;
var responseLength = response.ContentLength;
responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false); responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false);
var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData; var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData;
@@ -472,18 +475,18 @@ namespace CryptoExchange.Net.Clients
if (error.Code == null || error.Code == 0) if (error.Code == null || error.Code == 0)
error.Code = (int)response.StatusCode; error.Code = (int)response.StatusCode;
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!);
} }
var valid = await accessor.Read(responseStream, outputOriginalData).ConfigureAwait(false); var valid = await accessor.Read(responseStream, outputOriginalData).ConfigureAwait(false);
if (typeof(T) == typeof(object)) if (typeof(T) == typeof(object))
// Success status code and expected empty response, assume it's correct // Success status code and expected empty response, assume it's correct
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null); return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
if (!valid) if (!valid)
{ {
// Invalid json // Invalid json
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error);
} }
// Json response received // Json response received
@@ -500,55 +503,33 @@ namespace CryptoExchange.Net.Clients
} }
// Success status code, but TryParseError determined it was an error response // Success status code, but TryParseError determined it was an error response
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
} }
var deserializeResult = accessor.Deserialize<T>(); var deserializeResult = accessor.Deserialize<T>();
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
} }
catch (HttpRequestException requestException) catch (HttpRequestException requestException)
{ {
// Request exception, can't reach server for instance // Request exception, can't reach server for instance
var error = new WebError(requestException.Message, requestException); var error = new WebError(requestException.Message, requestException);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
} }
catch (OperationCanceledException canceledException) catch (OperationCanceledException canceledException)
{ {
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken) if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
{ {
// Cancellation token canceled by caller // Cancellation token canceled by caller
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException)); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException));
} }
else else
{ {
// Request timed out // Request timed out
var error = new WebError($"Request timed out", exception: canceledException); var error = new WebError($"Request timed out", exception: canceledException);
error.ErrorType = ErrorType.Timeout; error.ErrorType = ErrorType.Timeout;
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
} }
} }
catch (ArgumentException argumentException)
{
if (argumentException.Message.StartsWith("Only HTTP/"))
{
// Unsupported HTTP version error .net framework
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version {request.HttpVersion}: " + argumentException.Message);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
}
throw;
}
catch (NotSupportedException notSupportedException)
{
if (notSupportedException.Message.StartsWith("Request version value must be one of"))
{
// Unsupported HTTP version error dotnet code
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version {request.HttpVersion}: " + notSupportedException.Message);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
}
throw;
}
finally finally
{ {
accessor?.Clear(); accessor?.Clear();
@@ -656,7 +637,7 @@ namespace CryptoExchange.Net.Clients
{ {
// Handle retry after header // Handle retry after header
var retryAfterHeader = responseHeaders.SingleOrDefault(r => r.Key.Equals("Retry-After", StringComparison.InvariantCultureIgnoreCase)); var retryAfterHeader = responseHeaders.SingleOrDefault(r => r.Key.Equals("Retry-After", StringComparison.InvariantCultureIgnoreCase));
if (retryAfterHeader.Value?.Any() != true) if (!(retryAfterHeader.Value.Length > 0))
return new ServerRateLimitError(); return new ServerRateLimitError();
var value = retryAfterHeader.Value.First(); var value = retryAfterHeader.Value.First();
@@ -693,21 +674,21 @@ namespace CryptoExchange.Net.Clients
{ {
base.SetOptions(options); base.SetOptions(options);
RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout, ClientOptions.HttpKeepAliveInterval); RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout);
} }
internal async Task<WebCallResult<bool>> SyncTimeAsync() internal async Task<WebCallResult<bool>> SyncTimeAsync()
{ {
var timeSyncParams = GetTimeSyncInfo(); var timeSyncParams = GetTimeSyncInfo();
if (timeSyncParams == null) if (timeSyncParams == null)
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false)) if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
{ {
if (!timeSyncParams.SyncTime || DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval) if (!timeSyncParams.SyncTime || DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval)
{ {
timeSyncParams.TimeSyncState.Semaphore.Release(); timeSyncParams.TimeSyncState.Semaphore.Release();
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
} }
var localTime = DateTime.UtcNow; var localTime = DateTime.UtcNow;
@@ -736,12 +717,11 @@ namespace CryptoExchange.Net.Clients
timeSyncParams.TimeSyncState.Semaphore.Release(); timeSyncParams.TimeSyncState.Semaphore.Release();
} }
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
} }
private bool ShouldCache(RequestDefinition definition) private bool ShouldCache(RequestDefinition definition)
=> ClientOptions.CachingEnabled => ClientOptions.CachingEnabled
&& definition.Method == HttpMethod.Get && definition.Method == HttpMethod.Get
&& !definition.PreventCaching; && !definition.PreventCaching;
}
} }
+42 -64
View File
@@ -17,13 +17,13 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base socket API client for interaction with a websocket API
/// </summary>
public abstract class SocketApiClient : BaseApiClient, ISocketApiClient
{ {
/// <summary>
/// Base socket API client for interaction with a websocket API
/// </summary>
public abstract class SocketApiClient : BaseApiClient, ISocketApiClient
{
#region Fields #region Fields
/// <inheritdoc/> /// <inheritdoc/>
public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory(); public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory();
@@ -86,7 +86,7 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Whether to continue processing and forward unparsable messages to handlers /// Whether to continue processing and forward unparsable messages to handlers
/// </summary> /// </summary>
protected internal bool ProcessUnparsableMessages { get; set; } = false; protected internal bool ProcessUnparsableMessages { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public double IncomingKbps public double IncomingKbps
@@ -227,7 +227,7 @@ namespace CryptoExchange.Net.Clients
while (true) while (true)
{ {
// Get a new or existing socket connection // Get a new or existing socket connection
var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, ct, subscription.Topic).ConfigureAwait(false); var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, subscription.Topic).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.As<UpdateSubscription>(null); return socketResult.As<UpdateSubscription>(null);
@@ -269,7 +269,6 @@ namespace CryptoExchange.Net.Clients
return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused"))); return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
} }
subscription.Status = SubscriptionStatus.Subscribing;
var waitEvent = new AsyncResetEvent(false); var waitEvent = new AsyncResetEvent(false);
var subQuery = subscription.CreateSubscriptionQuery(socketConnection); var subQuery = subscription.CreateSubscriptionQuery(socketConnection);
if (subQuery != null) if (subQuery != null)
@@ -280,7 +279,7 @@ namespace CryptoExchange.Net.Clients
{ {
waitEvent?.Set(); waitEvent?.Set();
var isTimeout = subResult.Error is CancellationRequestedError; var isTimeout = subResult.Error is CancellationRequestedError;
if (isTimeout && subscription.Status == SubscriptionStatus.Subscribed) if (isTimeout && subscription.Confirmed)
{ {
// No response received, but the subscription did receive updates. We'll assume success // No response received, but the subscription did receive updates. We'll assume success
} }
@@ -288,7 +287,6 @@ namespace CryptoExchange.Net.Clients
{ {
_logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString()); _logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString());
// If this was a timeout we still need to send an unsubscribe to prevent messages coming in later // If this was a timeout we still need to send an unsubscribe to prevent messages coming in later
subscription.Status = SubscriptionStatus.Pending;
await socketConnection.CloseAsync(subscription).ConfigureAwait(false); await socketConnection.CloseAsync(subscription).ConfigureAwait(false);
return new CallResult<UpdateSubscription>(subResult.Error!); return new CallResult<UpdateSubscription>(subResult.Error!);
} }
@@ -297,7 +295,7 @@ namespace CryptoExchange.Net.Clients
subscription.HandleSubQueryResponse(subQuery.Response!); subscription.HandleSubQueryResponse(subQuery.Response!);
} }
subscription.Status = SubscriptionStatus.Subscribed; subscription.Confirmed = true;
if (ct != default) if (ct != default)
{ {
subscription.CancellationTokenRegistration = ct.Register(async () => subscription.CancellationTokenRegistration = ct.Register(async () =>
@@ -342,10 +340,16 @@ namespace CryptoExchange.Net.Clients
SocketConnection socketConnection; SocketConnection socketConnection;
var released = false; var released = false;
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
try try
{ {
var socketResult = await GetSocketConnection(url, query.Authenticated, true, ct).ConfigureAwait(false); await semaphoreSlim.WaitAsync(ct).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
try
{
var socketResult = await GetSocketConnection(url, query.Authenticated, true).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.As<THandlerResponse>(default); return socketResult.As<THandlerResponse>(default);
@@ -397,7 +401,13 @@ namespace CryptoExchange.Net.Clients
return connectResult; return connectResult;
if (ClientOptions.DelayAfterConnect != TimeSpan.Zero) if (ClientOptions.DelayAfterConnect != TimeSpan.Zero)
await Task.Delay(ClientOptions.DelayAfterConnect).ConfigureAwait(false); {
try
{
await Task.Delay(ClientOptions.DelayAfterConnect, ct).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
}
if (!authenticated || socket.Authenticated) if (!authenticated || socket.Authenticated)
return CallResult.SuccessResult; return CallResult.SuccessResult;
@@ -496,56 +506,25 @@ namespace CryptoExchange.Net.Clients
/// <param name="address">The address the socket is for</param> /// <param name="address">The address the socket is for</param>
/// <param name="authenticated">Whether the socket should be authenticated</param> /// <param name="authenticated">Whether the socket should be authenticated</param>
/// <param name="dedicatedRequestConnection">Whether a dedicated request connection should be returned</param> /// <param name="dedicatedRequestConnection">Whether a dedicated request connection should be returned</param>
/// <param name="ct">Cancellation token</param>
/// <param name="topic">The subscription topic, can be provided when multiple of the same topics are not allowed on a connection</param> /// <param name="topic">The subscription topic, can be provided when multiple of the same topics are not allowed on a connection</param>
/// <returns></returns> /// <returns></returns>
protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, CancellationToken ct, string? topic = null) protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, string? topic = null)
{ {
var socketQuery = socketConnections.Where(s => s.Value.Tag.TrimEnd('/') == address.TrimEnd('/') var socketQuery = socketConnections.Where(s => (s.Value.Status == SocketConnection.SocketStatus.None || s.Value.Status == SocketConnection.SocketStatus.Connected)
&& s.Value.Tag.TrimEnd('/') == address.TrimEnd('/')
&& s.Value.ApiClient.GetType() == GetType() && s.Value.ApiClient.GetType() == GetType()
&& (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic))) && (s.Value.Authenticated == authenticated || !authenticated)
.Select(x => x.Value) && (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic))
.ToList(); && s.Value.Connected);
// If all current socket connections are reconnecting or resubscribing wait for that to finish as we can probably use the existing connection SocketConnection connection;
var delayStart = DateTime.UtcNow;
var delayed = false;
while (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
{
if (DateTime.UtcNow - delayStart > TimeSpan.FromSeconds(10))
{
if (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
{
// If after this time we still trying to reconnect/reprocess there is some issue in the connection
_logger.TimeoutWaitingForReconnectingSocket();
return new CallResult<SocketConnection>(new CantConnectError());
}
break;
}
delayed = true;
try { await Task.Delay(50, ct).ConfigureAwait(false); } catch (Exception) { }
if (ct.IsCancellationRequested)
return new CallResult<SocketConnection>(new CancellationRequestedError());
}
if (delayed)
_logger.WaitedForReconnectingSocket((long)(DateTime.UtcNow - delayStart).TotalMilliseconds);
socketQuery = socketQuery.Where(s => (s.Status == SocketConnection.SocketStatus.None || s.Status == SocketConnection.SocketStatus.Connected)
&& (s.Authenticated == authenticated || !authenticated)
&& s.Connected).ToList();
SocketConnection? connection;
if (!dedicatedRequestConnection) if (!dedicatedRequestConnection)
{ {
connection = socketQuery.Where(s => !s.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.UserSubscriptionCount).FirstOrDefault(); connection = socketQuery.Where(s => !s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.Value.UserSubscriptionCount).FirstOrDefault().Value;
} }
else else
{ {
connection = socketQuery.Where(s => s.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault(); connection = socketQuery.Where(s => s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault().Value;
if (connection != null && !connection.DedicatedRequestConnection.Authenticated) if (connection != null && !connection.DedicatedRequestConnection.Authenticated)
// Mark dedicated request connection as authenticated if the request is authenticated // Mark dedicated request connection as authenticated if the request is authenticated
connection.DedicatedRequestConnection.Authenticated = authenticated; connection.DedicatedRequestConnection.Authenticated = authenticated;
@@ -553,13 +532,10 @@ namespace CryptoExchange.Net.Clients
if (connection != null) if (connection != null)
{ {
if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
|| (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
{
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new // Use existing socket if it has less than target connections OR it has the least connections and we can't make new
return new CallResult<SocketConnection>(connection); return new CallResult<SocketConnection>(connection);
} }
}
var connectionAddress = await GetConnectionUrlAsync(address, authenticated).ConfigureAwait(false); var connectionAddress = await GetConnectionUrlAsync(address, authenticated).ConfigureAwait(false);
if (!connectionAddress) if (!connectionAddress)
@@ -752,7 +728,7 @@ namespace CryptoExchange.Net.Clients
{ {
foreach (var item in DedicatedConnectionConfigs) foreach (var item in DedicatedConnectionConfigs)
{ {
var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true, CancellationToken.None).ConfigureAwait(false); var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.AsDataless(); return socketResult.AsDataless();
@@ -849,7 +825,7 @@ namespace CryptoExchange.Net.Clients
cs.SubscriptionStates.ForEach(subState => cs.SubscriptionStates.ForEach(subState =>
{ {
sb.AppendLine($"\t\t\tId: {subState.Id}"); sb.AppendLine($"\t\t\tId: {subState.Id}");
sb.AppendLine($"\t\t\tStatus: {subState.Status}"); sb.AppendLine($"\t\t\tConfirmed: {subState.Confirmed}");
sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}"); sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}");
sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]"); sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]");
}); });
@@ -863,8 +839,11 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose the client /// Dispose the client
/// </summary> /// </summary>
public override void Dispose() public override void Dispose(bool disposing)
{ {
if (disposing)
return;
_disposing = true; _disposing = true;
var tasks = new List<Task>(); var tasks = new List<Task>();
{ {
@@ -879,7 +858,7 @@ namespace CryptoExchange.Net.Clients
} }
semaphoreSlim?.Dispose(); semaphoreSlim?.Dispose();
base.Dispose(); base.Dispose(disposing);
} }
/// <summary> /// <summary>
@@ -897,5 +876,4 @@ namespace CryptoExchange.Net.Clients
/// <param name="data"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
public virtual ReadOnlyMemory<byte> PreprocessStreamMessage(SocketConnection connection, WebSocketMessageType type, ReadOnlyMemory<byte> data) => data; public virtual ReadOnlyMemory<byte> PreprocessStreamMessage(SocketConnection connection, WebSocketMessageType type, ReadOnlyMemory<byte> data) => data;
}
} }
@@ -1,13 +1,13 @@
using System; using System;
namespace CryptoExchange.Net.Converters namespace CryptoExchange.Net.Converters;
/// <summary>
/// Mark property as an index in the array
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ArrayPropertyAttribute : Attribute
{ {
/// <summary>
/// Mark property as an index in the array
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ArrayPropertyAttribute : Attribute
{
/// <summary> /// <summary>
/// The index in the array /// The index in the array
/// </summary> /// </summary>
@@ -21,5 +21,4 @@ namespace CryptoExchange.Net.Converters
{ {
Index = index; Index = index;
} }
}
} }
@@ -1,16 +1,14 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters namespace CryptoExchange.Net.Converters;
/// <summary>
/// Caching for JsonSerializerContext instances
/// </summary>
public static class JsonSerializerContextCache
{ {
/// <summary>
/// Caching for JsonSerializerContext instances
/// </summary>
public static class JsonSerializerContextCache
{
private static ConcurrentDictionary<Type, JsonSerializerContext> _cache = new ConcurrentDictionary<Type, JsonSerializerContext>(); private static ConcurrentDictionary<Type, JsonSerializerContext> _cache = new ConcurrentDictionary<Type, JsonSerializerContext>();
/// <summary> /// <summary>
@@ -27,5 +25,4 @@ namespace CryptoExchange.Net.Converters
_cache[contextType] = instance; _cache[contextType] = instance;
return instance; return instance;
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Node accessor
/// </summary>
public readonly struct NodeAccessor
{ {
/// <summary>
/// Node accessor
/// </summary>
public readonly struct NodeAccessor
{
/// <summary> /// <summary>
/// Index /// Index
/// </summary> /// </summary>
@@ -45,5 +45,4 @@
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static NodeAccessor PropertyName() { return new NodeAccessor(null, null, 2); } public static NodeAccessor PropertyName() { return new NodeAccessor(null, null, 2); }
}
} }
@@ -1,13 +1,13 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message access definition
/// </summary>
public readonly struct MessagePath : IEnumerable<NodeAccessor>
{ {
/// <summary>
/// Message access definition
/// </summary>
public readonly struct MessagePath : IEnumerable<NodeAccessor>
{
private readonly List<NodeAccessor> _path; private readonly List<NodeAccessor> _path;
internal void Add(NodeAccessor node) internal void Add(NodeAccessor node)
@@ -46,5 +46,4 @@ namespace CryptoExchange.Net.Converters.MessageParsing
{ {
return GetEnumerator(); return GetEnumerator();
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message path extension methods
/// </summary>
public static class MessagePathExtension
{ {
/// <summary>
/// Message path extension methods
/// </summary>
public static class MessagePathExtension
{
/// <summary> /// <summary>
/// Add a string node accessor /// Add a string node accessor
/// </summary> /// </summary>
@@ -39,5 +39,4 @@
path.Add(NodeAccessor.Int(index)); path.Add(NodeAccessor.Int(index));
return path; return path;
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message node type
/// </summary>
public enum NodeType
{ {
/// <summary>
/// Message node type
/// </summary>
public enum NodeType
{
/// <summary> /// <summary>
/// Array node /// Array node
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Value node /// Value node
/// </summary> /// </summary>
Value Value
}
} }
@@ -1,28 +1,27 @@
using System; using System;
using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
using CryptoExchange.Net.Attributes;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Diagnostics;
namespace CryptoExchange.Net.Converters.SystemTextJson
{
/// <summary>
/// Converter for arrays to objects. Can deserialize data like [0.1, 0.2, "test"] to an object. Mapping is done by marking the class with [JsonConverter(typeof(ArrayConverter))] and the properties
/// with [ArrayProperty(x)] where x is the index of the property in the array
/// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class ArrayConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : JsonConverter<T> where T : new() using System.Diagnostics.CodeAnalysis;
#else
public class ArrayConverter<T> : JsonConverter<T> where T : new()
#endif #endif
{ using System.Threading;
namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for arrays to objects. Can deserialize data like [0.1, 0.2, "test"] to an object. Mapping is done by marking the class with [JsonConverter(typeof(ArrayConverter))] and the properties
/// with [ArrayProperty(x)] where x is the index of the property in the array
/// </summary>
#if NET5_0_OR_GREATER
public class ArrayConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : JsonConverter<T> where T : new()
#else
public class ArrayConverter<T> : JsonConverter<T> where T : new()
#endif
{
private static readonly Lazy<List<ArrayPropertyInfo>> _typePropertyInfo = new Lazy<List<ArrayPropertyInfo>>(CacheTypeAttributes, LazyThreadSafetyMode.PublicationOnly); private static readonly Lazy<List<ArrayPropertyInfo>> _typePropertyInfo = new Lazy<List<ArrayPropertyInfo>>(CacheTypeAttributes, LazyThreadSafetyMode.PublicationOnly);
/// <inheritdoc /> /// <inheritdoc />
@@ -228,7 +227,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public JsonConverter? JsonConverter { get; set; } public JsonConverter? JsonConverter { get; set; }
public bool DefaultDeserialization { get; set; } public bool DefaultDeserialization { get; set; }
public Type TargetType { get; set; } = null!; public Type TargetType { get; set; } = null!;
public JsonSerializerOptions? JsonSerializerOptions { get; set; } = null; public JsonSerializerOptions? JsonSerializerOptions { get; set; }
}
} }
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Decimal converter that handles overflowing decimal values (by setting it to decimal.MaxValue)
/// </summary>
public class BigDecimalConverter : JsonConverter<decimal>
{ {
/// <summary>
/// Decimal converter that handles overflowing decimal values (by setting it to decimal.MaxValue)
/// </summary>
public class BigDecimalConverter : JsonConverter<decimal>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -42,5 +42,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteNumberValue(value); writer.WriteNumberValue(value);
} }
}
} }
@@ -1,17 +1,16 @@
using Microsoft.Extensions.Logging;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Bool converter
/// </summary>
public class BoolConverter : JsonConverterFactory
{ {
/// <summary>
/// Bool converter
/// </summary>
public class BoolConverter : JsonConverterFactory
{
/// <inheritdoc /> /// <inheritdoc />
public override bool CanConvert(Type typeToConvert) public override bool CanConvert(Type typeToConvert)
{ {
@@ -29,7 +28,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)
=> (T)((object?)ReadBool(ref reader, typeToConvert, options) ?? default(T))!; => (T)((object?)ReadBool(ref reader, typeToConvert, options) ?? default(T))!;
public bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public static bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.True) if (reader.TokenType == JsonTokenType.True)
return true; return true;
@@ -48,7 +47,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
if (typeToConvert == typeof(bool)) if (typeToConvert == typeof(bool))
LibraryHelpers.StaticLogger?.LogWarning("Received null bool value, but property type is not a nullable bool"); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null bool value, but property type is not a nullable bool");
return default; return default;
} }
@@ -81,5 +80,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
} }
}
} }
@@ -1,23 +1,23 @@
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.Linq; using System.Linq;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
{
/// <summary> /// <summary>
/// Converter for comma separated enum values /// Converter for comma separated enum values
/// </summary> /// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class CommaSplitEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> : JsonConverter<T[]> where T : struct, Enum public class CommaSplitEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> : JsonConverter<T[]> where T : struct, Enum
#else #else
public class CommaSplitEnumConverter<T> : JsonConverter<T[]> where T : struct, Enum public class CommaSplitEnumConverter<T> : JsonConverter<T[]> where T : struct, Enum
#endif #endif
{ {
/// <inheritdoc /> /// <inheritdoc />
public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -33,5 +33,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteStringValue(string.Join(",", value.Select(x => EnumConverter.GetString(x)))); writer.WriteStringValue(string.Join(",", value.Select(x => EnumConverter.GetString(x))));
} }
}
} }
@@ -1,4 +1,3 @@
using Microsoft.Extensions.Logging;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -6,17 +5,17 @@ using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Date time converter
/// </summary>
public class DateTimeConverter : JsonConverterFactory
{ {
/// <summary>
/// Date time converter
/// </summary>
public class DateTimeConverter : JsonConverterFactory
{
private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000; private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000;
private const decimal _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000m; private const double _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000d;
private const decimal _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000m / 1000; private const double _ticksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000d / 1000;
/// <inheritdoc /> /// <inheritdoc />
public override bool CanConvert(Type typeToConvert) public override bool CanConvert(Type typeToConvert)
@@ -35,22 +34,22 @@ 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)
=> (T)((object?)ReadDateTime(ref reader, typeToConvert, options) ?? default(T))!; => (T)((object?)ReadDateTime(ref reader, typeToConvert, options) ?? default(T))!;
private DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) private static DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.Null) if (reader.TokenType == JsonTokenType.Null)
{ {
if (typeToConvert == typeof(DateTime)) if (typeToConvert == typeof(DateTime))
LibraryHelpers.StaticLogger?.LogWarning("DateTime value of null, but property is not nullable"); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | DateTime value of null, but property is not nullable");
return default; return default;
} }
if (reader.TokenType is JsonTokenType.Number) if (reader.TokenType is JsonTokenType.Number)
{ {
var decValue = reader.GetDecimal(); var longValue = reader.GetDouble();
if (decValue == 0 || decValue < 0) if (longValue == 0 || longValue < 0)
return default; return default;
return ParseFromDecimal(decValue); return ParseFromDouble(longValue);
} }
else if (reader.TokenType is JsonTokenType.String) else if (reader.TokenType is JsonTokenType.String)
{ {
@@ -58,7 +57,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrWhiteSpace(stringValue) if (string.IsNullOrWhiteSpace(stringValue)
|| stringValue == "-1" || stringValue == "-1"
|| stringValue == "0001-01-01T00:00:00Z" || stringValue == "0001-01-01T00:00:00Z"
|| decimal.TryParse(stringValue, out var decVal) && decVal == 0) || double.TryParse(stringValue, out var doubleVal) && doubleVal == 0)
{ {
return default; return default;
} }
@@ -89,24 +88,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
/// <summary> /// <summary>
/// Parse a double value to datetime /// Parse a long value to datetime
/// </summary> /// </summary>
public static DateTime ParseFromDouble(double value) /// <param name="longValue"></param>
=> ParseFromDecimal((decimal)value); /// <returns></returns>
public static DateTime ParseFromDouble(double longValue)
/// <summary>
/// Parse a decimal value to datetime
/// </summary>
public static DateTime ParseFromDecimal(decimal value)
{ {
if (value < 19999999999) if (longValue < 19999999999)
return ConvertFromSeconds(value); return ConvertFromSeconds(longValue);
if (value < 19999999999999) if (longValue < 19999999999999)
return ConvertFromMilliseconds(value); return ConvertFromMilliseconds(longValue);
if (value < 19999999999999999) if (longValue < 19999999999999999)
return ConvertFromMicroseconds(value); return ConvertFromMicroseconds(longValue);
return ConvertFromNanoseconds(value); return ConvertFromNanoseconds(longValue);
} }
/// <summary> /// <summary>
@@ -125,7 +120,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(8, 2), out var hour) || !int.TryParse(stringValue.Substring(8, 2), out var hour)
|| !int.TryParse(stringValue.Substring(10, 2), out var minute)) || !int.TryParse(stringValue.Substring(10, 2), out var minute))
{ {
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc); return new DateTime(year, month, day, hour, minute, 0, DateTimeKind.Utc);
@@ -138,7 +133,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(4, 2), out var month) || !int.TryParse(stringValue.Substring(4, 2), out var month)
|| !int.TryParse(stringValue.Substring(6, 2), out var day)) || !int.TryParse(stringValue.Substring(6, 2), out var day))
{ {
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc); return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
@@ -151,25 +146,25 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(stringValue.Substring(2, 2), out var month) || !int.TryParse(stringValue.Substring(2, 2), out var month)
|| !int.TryParse(stringValue.Substring(4, 2), out var day)) || !int.TryParse(stringValue.Substring(4, 2), out var day))
{ {
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue); Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
return default; return default;
} }
return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc); return new DateTime(year + 2000, month, day, 0, 0, 0, DateTimeKind.Utc);
} }
if (decimal.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var decimalValue)) if (double.TryParse(stringValue, NumberStyles.Float, CultureInfo.InvariantCulture, out var doubleValue))
{ {
// Parse 1637745563.000 format // Parse 1637745563.000 format
if (decimalValue <= 0) if (doubleValue <= 0)
return default; return default;
if (decimalValue < 19999999999) if (doubleValue < 19999999999)
return ConvertFromSeconds(decimalValue); return ConvertFromSeconds(doubleValue);
if (decimalValue < 19999999999999) if (doubleValue < 19999999999999)
return ConvertFromMilliseconds(decimalValue); return ConvertFromMilliseconds((long)doubleValue);
if (decimalValue < 19999999999999999) if (doubleValue < 19999999999999999)
return ConvertFromMicroseconds(decimalValue); return ConvertFromMicroseconds((long)doubleValue);
return ConvertFromNanoseconds(decimalValue); return ConvertFromNanoseconds((long)doubleValue);
} }
if (stringValue.Length == 10) if (stringValue.Length == 10)
@@ -180,7 +175,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|| !int.TryParse(values[1], out var month) || !int.TryParse(values[1], out var month)
|| !int.TryParse(values[2], out var day)) || !int.TryParse(values[2], out var day))
{ {
LibraryHelpers.StaticLogger?.LogWarning("Unknown DateTime format: " + stringValue); Trace.WriteLine("{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Unknown DateTime format: " + stringValue);
return default; return default;
} }
@@ -193,71 +188,54 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <summary> /// <summary>
/// Convert a seconds since epoch (01-01-1970) value to DateTime /// Convert a seconds since epoch (01-01-1970) value to DateTime
/// </summary> /// </summary>
public static DateTime ConvertFromSeconds(decimal seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond)); /// <param name="seconds"></param>
/// <summary> /// <returns></returns>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime public static DateTime ConvertFromSeconds(double seconds) => _epoch.AddTicks((long)Math.Round(seconds * _ticksPerSecond));
/// </summary>
public static DateTime ConvertFromSeconds(double seconds) => ConvertFromSeconds((decimal)seconds);
/// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary>
public static DateTime ConvertFromSeconds(long seconds) => ConvertFromSeconds((decimal)seconds);
/// <summary> /// <summary>
/// Convert a milliseconds since epoch (01-01-1970) value to DateTime /// Convert a milliseconds since epoch (01-01-1970) value to DateTime
/// </summary> /// </summary>
public static DateTime ConvertFromMilliseconds(decimal milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond)); /// <param name="milliseconds"></param>
/// <summary> /// <returns></returns>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime public static DateTime ConvertFromMilliseconds(double milliseconds) => _epoch.AddTicks((long)Math.Round(milliseconds * TimeSpan.TicksPerMillisecond));
/// </summary>
public static DateTime ConvertFromMilliseconds(double milliseconds) => ConvertFromMilliseconds((decimal)milliseconds);
/// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary>
public static DateTime ConvertFromMilliseconds(long milliseconds) => ConvertFromMilliseconds((decimal)milliseconds);
/// <summary> /// <summary>
/// Convert a microseconds since epoch (01-01-1970) value to DateTime /// Convert a microseconds since epoch (01-01-1970) value to DateTime
/// </summary> /// </summary>
public static DateTime ConvertFromMicroseconds(decimal microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * _ticksPerMicrosecond)); /// <param name="microseconds"></param>
/// <returns></returns>
public static DateTime ConvertFromMicroseconds(double microseconds) => _epoch.AddTicks((long)Math.Round(microseconds * _ticksPerMicrosecond));
/// <summary> /// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime /// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary> /// </summary>
public static DateTime ConvertFromMicroseconds(double microseconds) => ConvertFromMicroseconds((decimal)microseconds); /// <param name="nanoseconds"></param>
/// <summary> /// <returns></returns>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime public static DateTime ConvertFromNanoseconds(double nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * _ticksPerNanosecond));
/// </summary>
public static DateTime ConvertFromMicroseconds(long microseconds) => ConvertFromMicroseconds((decimal)microseconds);
/// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary>
public static DateTime ConvertFromNanoseconds(decimal nanoseconds) => _epoch.AddTicks((long)Math.Round(nanoseconds * _ticksPerNanosecond));
/// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary>
public static DateTime ConvertFromNanoseconds(double nanoseconds) => ConvertFromNanoseconds((decimal)nanoseconds);
/// <summary>
/// Convert a nanoseconds since epoch (01-01-1970) value to DateTime
/// </summary>
public static DateTime ConvertFromNanoseconds(long nanoseconds) => ConvertFromNanoseconds((decimal)nanoseconds);
/// <summary> /// <summary>
/// Convert a DateTime value to seconds since epoch (01-01-1970) value /// Convert a DateTime value to seconds since epoch (01-01-1970) value
/// </summary> /// </summary>
/// <param name="time"></param>
/// <returns></returns>
[return: NotNullIfNotNull("time")] [return: NotNullIfNotNull("time")]
public static long? ConvertToSeconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalSeconds); public static long? ConvertToSeconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalSeconds);
/// <summary> /// <summary>
/// Convert a DateTime value to milliseconds since epoch (01-01-1970) value /// Convert a DateTime value to milliseconds since epoch (01-01-1970) value
/// </summary> /// </summary>
/// <param name="time"></param>
/// <returns></returns>
[return: NotNullIfNotNull("time")] [return: NotNullIfNotNull("time")]
public static long? ConvertToMilliseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalMilliseconds); public static long? ConvertToMilliseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).TotalMilliseconds);
/// <summary> /// <summary>
/// Convert a DateTime value to microseconds since epoch (01-01-1970) value /// Convert a DateTime value to microseconds since epoch (01-01-1970) value
/// </summary> /// </summary>
/// <param name="time"></param>
/// <returns></returns>
[return: NotNullIfNotNull("time")] [return: NotNullIfNotNull("time")]
public static long? ConvertToMicroseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerMicrosecond); public static long? ConvertToMicroseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerMicrosecond);
/// <summary> /// <summary>
/// Convert a DateTime value to nanoseconds since epoch (01-01-1970) value /// Convert a DateTime value to nanoseconds since epoch (01-01-1970) value
/// </summary> /// </summary>
/// <param name="time"></param>
/// <returns></returns>
[return: NotNullIfNotNull("time")] [return: NotNullIfNotNull("time")]
public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond); public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond);
}
} }
@@ -1,15 +1,14 @@
using System; using System;
using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Decimal converter
/// </summary>
public class DecimalConverter : JsonConverter<decimal?>
{ {
/// <summary>
/// Decimal converter
/// </summary>
public class DecimalConverter : JsonConverter<decimal?>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -41,5 +40,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for serializing decimal values as string
/// </summary>
public class DecimalStringWriterConverter : JsonConverter<decimal>
{ {
/// <summary>
/// Converter for serializing decimal values as string
/// </summary>
public class DecimalStringWriterConverter : JsonConverter<decimal>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -19,5 +19,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture) ?? null); => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture) ?? null);
}
} }
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Attributes; using CryptoExchange.Net.Attributes;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -11,13 +9,13 @@ using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Static EnumConverter methods
/// </summary>
public static class EnumConverter
{ {
/// <summary>
/// Static EnumConverter methods
/// </summary>
public static class EnumConverter
{
/// <summary> /// <summary>
/// Get the enum value from a string /// Get the enum value from a string
/// </summary> /// </summary>
@@ -54,20 +52,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public static string? GetString<T>(T? enumValue) where T : struct, Enum public static string? GetString<T>(T? enumValue) where T : struct, Enum
#endif #endif
=> EnumConverter<T>.GetString(enumValue); => EnumConverter<T>.GetString(enumValue);
} }
/// <summary> /// <summary>
/// Converter for enum values. Enums entries should be noted with a MapAttribute to map the enum value to a string value /// Converter for enum values. Enums entries should be noted with a MapAttribute to map the enum value to a string value
/// </summary> /// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class EnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> public class EnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>
#else #else
public class EnumConverter<T> public class EnumConverter<T>
#endif #endif
: JsonConverter<T>, INullableConverterFactory where T : struct, Enum : JsonConverter<T>, INullableConverterFactory where T : struct, Enum
{ {
private static List<KeyValuePair<T, string>>? _mapping = null; private static List<KeyValuePair<T, string>>? _mapping;
private NullableEnumConverter? _nullableEnumConverter = null; private NullableEnumConverter? _nullableEnumConverter;
private static ConcurrentBag<string> _unknownValuesWarned = new ConcurrentBag<string>(); private static ConcurrentBag<string> _unknownValuesWarned = new ConcurrentBag<string>();
@@ -81,7 +79,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString); return EnumConverter<T>.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn);
} }
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
@@ -100,13 +98,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString); var t = ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn);
if (t == null) if (t == null)
{ {
if (isEmptyString && !_unknownValuesWarned.Contains(null)) if (warn)
{
if (isEmptyString)
{ {
// We received an empty string and have no mapping for it, and the property isn't nullable // 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).FullName}. If you think {typeof(T).FullName} should be nullable please open an issue on the Github repo"); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received empty string as enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
}
else
{
Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Received null enum value, but property type is not a nullable enum. EnumType: {typeof(T).Name}. If you think {typeof(T).Name} should be nullable please open an issue on the Github repo");
}
} }
return new T(); // return default value return new T(); // return default value
@@ -117,9 +122,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
} }
private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString) private static T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn)
{ {
isEmptyString = false; isEmptyString = false;
warn = false;
var enumType = typeof(T); var enumType = typeof(T);
if (_mapping == null) if (_mapping == null)
_mapping = AddMapping(); _mapping = AddMapping();
@@ -134,10 +140,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_ => throw new Exception("Invalid token type for enum deserialization: " + reader.TokenType) _ => throw new Exception("Invalid token type for enum deserialization: " + reader.TokenType)
}; };
if (stringValue is null) if (string.IsNullOrEmpty(stringValue))
return null; return null;
if (!GetValue(enumType, stringValue, out var result)) if (!GetValue(enumType, stringValue!, out var result))
{ {
if (string.IsNullOrWhiteSpace(stringValue)) if (string.IsNullOrWhiteSpace(stringValue))
{ {
@@ -148,8 +154,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
// We received an enum value but weren't able to parse it. // We received an enum value but weren't able to parse it.
if (!_unknownValuesWarned.Contains(stringValue)) if (!_unknownValuesWarned.Contains(stringValue))
{ {
warn = true;
_unknownValuesWarned.Add(stringValue!); _unknownValuesWarned.Add(stringValue!);
LibraryHelpers.StaticLogger?.LogWarning($"Cannot map enum value. EnumType: {enumType.FullName}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo"); Trace.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss:fff} | Warning | Cannot map enum value. EnumType: {enumType.Name}, Value: {stringValue}, Known values: {string.Join(", ", _mapping.Select(m => m.Value))}. If you think {stringValue} should added please open an issue on the Github repo");
} }
} }
@@ -197,13 +204,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return false; return false;
} }
if (String.IsNullOrEmpty(value))
{
// An empty/null value will always fail when parsing, so just return here
result = default;
return false;
}
try try
{ {
// If no explicit mapping is found try to parse string // If no explicit mapping is found try to parse string
@@ -285,5 +285,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_nullableEnumConverter ??= new NullableEnumConverter(this); _nullableEnumConverter ??= new NullableEnumConverter(this);
return _nullableEnumConverter; return _nullableEnumConverter;
} }
}
} }
@@ -1,15 +1,14 @@
using System; using System;
using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for serializing enum values as int
/// </summary>
public class EnumIntWriterConverter<T> : JsonConverter<T> where T: struct, Enum
{ {
/// <summary>
/// Converter for serializing enum values as int
/// </summary>
public class EnumIntWriterConverter<T> : JsonConverter<T> where T: struct, Enum
{
/// <inheritdoc /> /// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -19,5 +18,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
=> writer.WriteNumberValue((int)(object)value); => writer.WriteNumberValue((int)(object)value);
}
} }
@@ -1,9 +1,8 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal interface INullableConverterFactory
{ {
internal interface INullableConverterFactory
{
JsonConverter CreateNullableConverter(); JsonConverter CreateNullableConverter();
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Int converter
/// </summary>
public class IntConverter : JsonConverter<int?>
{ {
/// <summary>
/// Int converter
/// </summary>
public class IntConverter : JsonConverter<int?>
{
/// <inheritdoc /> /// <inheritdoc />
public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -36,5 +36,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Int converter
/// </summary>
public class LongConverter : JsonConverter<long?>
{ {
/// <summary>
/// Int converter
/// </summary>
public class LongConverter : JsonConverter<long?>
{
/// <inheritdoc /> /// <inheritdoc />
public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -36,5 +36,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization.Metadata; using System.Text.Json.Serialization.Metadata;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal class NullableEnumConverterFactory : JsonConverterFactory
{ {
internal class NullableEnumConverterFactory : JsonConverterFactory
{
private readonly IJsonTypeInfoResolver _jsonTypeInfoResolver; private readonly IJsonTypeInfoResolver _jsonTypeInfoResolver;
private static readonly JsonSerializerOptions _options = new JsonSerializerOptions(); private static readonly JsonSerializerOptions _options = new JsonSerializerOptions();
@@ -39,5 +37,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return nullConverterFactory.CreateNullableConverter(); return nullConverterFactory.CreateNullableConverter();
} }
}
} }
@@ -1,14 +1,14 @@
using System; using System;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Read string or number as string
/// </summary>
public class NumberStringConverter : JsonConverter<string?>
{ {
/// <summary>
/// Read string or number as string
/// </summary>
public class NumberStringConverter : JsonConverter<string?>
{
/// <inheritdoc /> /// <inheritdoc />
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -38,5 +38,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteStringValue(value); writer.WriteStringValue(value);
} }
}
} }
@@ -1,15 +1,17 @@
using System; using System;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for values which contain a nested json value
/// </summary>
public class ObjectStringConverter<T> : JsonConverter<T>
{ {
/// <summary>
/// Converter for values which contain a nested json value
/// </summary>
public class ObjectStringConverter<T> : JsonConverter<T>
{
/// <inheritdoc /> /// <inheritdoc />
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")] [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")]
@@ -24,7 +26,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
return default; return default;
return (T?)JsonDocument.Parse(value!).Deserialize(typeof(T), options); return JsonDocument.Parse(value!).Deserialize<T>(options);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -39,5 +41,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteStringValue(JsonSerializer.Serialize(value, options)); writer.WriteStringValue(JsonSerializer.Serialize(value, options));
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Replace a value on a string property
/// </summary>
public abstract class ReplaceConverter : JsonConverter<string>
{ {
/// <summary>
/// Replace a value on a string property
/// </summary>
public abstract class ReplaceConverter : JsonConverter<string>
{
private readonly (string ValueToReplace, string ValueToReplaceWith)[] _replacementSets; private readonly (string ValueToReplace, string ValueToReplaceWith)[] _replacementSets;
/// <summary> /// <summary>
@@ -19,7 +19,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
_replacementSets = replaceSets.Select(x => _replacementSets = replaceSets.Select(x =>
{ {
var split = x.Split(new string[] { "->" }, StringSplitOptions.None); var split = x.Split(["->"], StringSplitOptions.None);
if (split.Length != 2) if (split.Length != 2)
throw new ArgumentException("Invalid replacement config"); throw new ArgumentException("Invalid replacement config");
return (split[0], split[1]); return (split[0], split[1]);
@@ -37,5 +37,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value); public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value);
}
} }
@@ -1,15 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Attribute to mark a model as json serializable. Used for AOT compilation.
/// </summary>
[AttributeUsage(System.AttributeTargets.Class | AttributeTargets.Enum | System.AttributeTargets.Interface)]
public class SerializationModelAttribute : Attribute
{ {
/// <summary>
/// Attribute to mark a model as json serializable. Used for AOT compilation.
/// </summary>
[AttributeUsage(System.AttributeTargets.Class | AttributeTargets.Enum | System.AttributeTargets.Interface)]
public class SerializationModelAttribute : Attribute
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -19,5 +17,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
public SerializationModelAttribute(Type type) { } public SerializationModelAttribute(Type type) { }
}
} }
@@ -1,14 +1,14 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Serializer options
/// </summary>
public static class SerializerOptions
{ {
/// <summary>
/// Serializer options
/// </summary>
public static class SerializerOptions
{
private static readonly ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions> _cache = new ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions>(); private static readonly ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions> _cache = new ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions>();
/// <summary> /// <summary>
@@ -43,5 +43,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return options; return options;
} }
}
} }
@@ -1,17 +1,15 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
{
internal class SharedQuantityConverter : SharedQuantityReferenceConverter<SharedQuantity> { }
internal class SharedOrderQuantityConverter : SharedQuantityReferenceConverter<SharedOrderQuantity> { }
internal class SharedQuantityReferenceConverter<T> : JsonConverter<T> where T: SharedQuantityReference, new() internal class SharedQuantityConverter : SharedQuantityReferenceConverter<SharedQuantity> { }
{ internal class SharedOrderQuantityConverter : SharedQuantityReferenceConverter<SharedOrderQuantity> { }
internal class SharedQuantityReferenceConverter<T> : JsonConverter<T> where T: SharedQuantityReference, new()
{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
@@ -56,5 +54,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteNumberValue(value.QuantityInContracts.Value); writer.WriteNumberValue(value.QuantityInContracts.Value);
writer.WriteEndArray(); writer.WriteEndArray();
} }
}
} }
@@ -1,14 +1,12 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal class SharedSymbolConverter : JsonConverter<SharedSymbol>
{ {
internal class SharedSymbolConverter : JsonConverter<SharedSymbol>
{
public override SharedSymbol? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override SharedSymbol? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
@@ -42,5 +40,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteStringValue(value.DeliverTime?.ToString()); writer.WriteStringValue(value.DeliverTime?.ToString());
writer.WriteEndArray(); writer.WriteEndArray();
} }
}
} }
@@ -1,21 +1,22 @@
using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// System.Text.Json message accessor
/// </summary>
public abstract class SystemTextJsonMessageAccessor : IMessageAccessor
{ {
/// <summary>
/// System.Text.Json message accessor
/// </summary>
public abstract class SystemTextJsonMessageAccessor : IMessageAccessor
{
/// <summary> /// <summary>
/// The JsonDocument loaded /// The JsonDocument loaded
/// </summary> /// </summary>
@@ -235,13 +236,15 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public abstract void Clear(); public abstract void Clear();
} }
/// <summary> /// <summary>
/// System.Text.Json stream message accessor /// System.Text.Json stream message accessor
/// </summary> /// </summary>
public class SystemTextJsonStreamMessageAccessor : SystemTextJsonMessageAccessor, IStreamMessageAccessor #pragma warning disable CA1001 // Types that own disposable fields should be disposable
{ public class SystemTextJsonStreamMessageAccessor : SystemTextJsonMessageAccessor, IStreamMessageAccessor
#pragma warning restore CA1001 // Types that own disposable fields should be disposable
{
private Stream? _stream; private Stream? _stream;
/// <inheritdoc /> /// <inheritdoc />
@@ -308,13 +311,13 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_document = null; _document = null;
} }
} }
/// <summary> /// <summary>
/// System.Text.Json byte message accessor /// System.Text.Json byte message accessor
/// </summary> /// </summary>
public class SystemTextJsonByteMessageAccessor : SystemTextJsonMessageAccessor, IByteMessageAccessor public class SystemTextJsonByteMessageAccessor : SystemTextJsonMessageAccessor, IByteMessageAccessor
{ {
private ReadOnlyMemory<byte> _bytes; private ReadOnlyMemory<byte> _bytes;
/// <summary> /// <summary>
@@ -370,5 +373,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_document?.Dispose(); _document?.Dispose();
_document = null; _document = null;
} }
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <inheritdoc />
public class SystemTextJsonMessageSerializer : IStringMessageSerializer
{ {
/// <inheritdoc />
public class SystemTextJsonMessageSerializer : IStringMessageSerializer
{
private readonly JsonSerializerOptions _options; private readonly JsonSerializerOptions _options;
/// <summary> /// <summary>
@@ -25,5 +25,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "Everything referenced in the loaded assembly is manually preserved, so it's safe")] [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "Everything referenced in the loaded assembly is manually preserved, so it's safe")]
#endif #endif
public string Serialize<T>(T message) => JsonSerializer.Serialize(message, _options); public string Serialize<T>(T message) => JsonSerializer.Serialize(message, _options);
}
} }
+17 -7
View File
@@ -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.13.0</PackageVersion> <PackageVersion>9.6.0</PackageVersion>
<AssemblyVersion>9.13.0</AssemblyVersion> <AssemblyVersion>9.6.0</AssemblyVersion>
<FileVersion>9.13.0</FileVersion> <FileVersion>9.6.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>
@@ -24,6 +24,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="C:\Projects\CryptoExchange.Net\CryptoExchange.Net\.editorconfig" />
<None Include="Icon\icon.png" Pack="true" PackagePath="\" /> <None Include="Icon\icon.png" Pack="true" PackagePath="\" />
<None Include="..\README.md" Pack="true" PackagePath="\" /> <None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup> </ItemGroup>
@@ -40,6 +41,12 @@
<PropertyGroup> <PropertyGroup>
<DocumentationFile>CryptoExchange.Net.xml</DocumentationFile> <DocumentationFile>CryptoExchange.Net.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>Recommended</AnalysisMode>
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1"> <PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@@ -51,11 +58,14 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.10" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageReference Include="System.Text.Json" Version="9.0.10" /> <PackageReference Include="System.Text.Json" Version="9.0.6" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Client Packages"> <ItemGroup Label="Transitive Client Packages">
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.10" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
</ItemGroup>
<ItemGroup>
<EditorConfigFiles Remove="C:\Projects\CryptoExchange.Net\CryptoExchange.Net\.editorconfig" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+16 -13
View File
@@ -1,20 +1,22 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER
using System.Security.Cryptography; using System.Security.Cryptography;
#endif
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// General helpers functions
/// </summary>
public static class ExchangeHelpers
{ {
/// <summary>
/// General helpers functions
/// </summary>
public static class ExchangeHelpers
{
private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789"; private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
private const string _allowedRandomHexChars = "0123456789ABCDEF"; private const string _allowedRandomHexChars = "0123456789ABCDEF";
@@ -89,6 +91,8 @@ namespace CryptoExchange.Net
else value += (step.Value - offset); else value += (step.Value - offset);
} }
value = RoundDown(value, 8);
return value.Normalize(); return value.Normalize();
} }
@@ -285,16 +289,16 @@ namespace CryptoExchange.Net
/// <summary> /// <summary>
/// Execute multiple requests to retrieve multiple pages of the result set /// Execute multiple requests to retrieve multiple pages of the result set
/// </summary> /// </summary>
/// <typeparam name="T">Type of the client</typeparam> /// <typeparam name="TResult">Type of the client</typeparam>
/// <typeparam name="U">Type of the request</typeparam> /// <typeparam name="TRequest">Type of the request</typeparam>
/// <param name="paginatedFunc">The func to execute with each request</param> /// <param name="paginatedFunc">The func to execute with each request</param>
/// <param name="request">The request parameters</param> /// <param name="request">The request parameters</param>
/// <param name="ct">Cancellation token</param> /// <param name="ct">Cancellation token</param>
/// <returns></returns> /// <returns></returns>
public static async IAsyncEnumerable<ExchangeWebResult<T[]>> ExecutePages<T, U>(Func<U, INextPageToken?, CancellationToken, Task<ExchangeWebResult<T[]>>> paginatedFunc, U request, [EnumeratorCancellation]CancellationToken ct = default) public static async IAsyncEnumerable<ExchangeWebResult<TResult[]>> ExecutePages<TResult, TRequest>(Func<TRequest, INextPageToken?, CancellationToken, Task<ExchangeWebResult<TResult[]>>> paginatedFunc, TRequest request, [EnumeratorCancellation]CancellationToken ct = default)
{ {
var result = new List<T>(); var result = new List<TResult>();
ExchangeWebResult<T[]> batch; ExchangeWebResult<TResult[]> batch;
INextPageToken? nextPageToken = null; INextPageToken? nextPageToken = null;
while (true) while (true)
{ {
@@ -383,5 +387,4 @@ namespace CryptoExchange.Net
// Unknown decimal format, return null // Unknown decimal format, return null
return null; return null;
} }
}
} }
+7 -9
View File
@@ -1,17 +1,16 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// Cache for symbol parsing
/// </summary>
public static class ExchangeSymbolCache
{ {
/// <summary>
/// Cache for symbol parsing
/// </summary>
public static class ExchangeSymbolCache
{
private static ConcurrentDictionary<string, ExchangeInfo> _symbolInfos = new ConcurrentDictionary<string, ExchangeInfo>(); private static ConcurrentDictionary<string, ExchangeInfo> _symbolInfos = new ConcurrentDictionary<string, ExchangeInfo>();
/// <summary> /// <summary>
@@ -66,5 +65,4 @@ namespace CryptoExchange.Net
Symbols = symbols; Symbols = symbols;
} }
} }
}
} }
+7 -43
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.IO; using System.IO;
@@ -10,17 +10,14 @@ using CryptoExchange.Net.Objects;
using System.Globalization; using System.Globalization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// Helper methods
/// </summary>
public static class ExtensionMethods
{ {
/// <summary>
/// Helper methods
/// </summary>
public static class ExtensionMethods
{
/// <summary> /// <summary>
/// Add a parameter /// Add a parameter
/// </summary> /// </summary>
@@ -416,36 +413,6 @@ namespace CryptoExchange.Net
/// </summary> /// </summary>
public static bool IsDelivery(this TradingMode type) => type == TradingMode.DeliveryInverse || type == TradingMode.DeliveryLinear; public static bool IsDelivery(this TradingMode type) => type == TradingMode.DeliveryInverse || type == TradingMode.DeliveryLinear;
/// <summary>
/// Whether the account type is a futures account
/// </summary>
public static bool IsFuturesAccount(this SharedAccountType type) =>
type == SharedAccountType.PerpetualLinearFutures
|| type == SharedAccountType.DeliveryLinearFutures
|| type == SharedAccountType.PerpetualInverseFutures
|| type == SharedAccountType.DeliveryInverseFutures;
/// <summary>
/// Whether the account type is a margin account
/// </summary>
public static bool IsMarginAccount(this SharedAccountType type) =>
type == SharedAccountType.CrossMargin
|| type == SharedAccountType.IsolatedMargin;
/// <summary>
/// Map a TradingMode value to a SharedAccountType enum value
/// </summary>
public static SharedAccountType ToAccountType(this TradingMode mode)
{
if (mode == TradingMode.Spot) return SharedAccountType.Spot;
if (mode == TradingMode.PerpetualLinear) return SharedAccountType.PerpetualLinearFutures;
if (mode == TradingMode.PerpetualInverse) return SharedAccountType.PerpetualInverseFutures;
if (mode == TradingMode.DeliveryInverse) return SharedAccountType.DeliveryInverseFutures;
if (mode == TradingMode.DeliveryLinear) return SharedAccountType.DeliveryLinearFutures;
throw new ArgumentException(nameof(mode), "Unmapped trading mode");
}
/// <summary> /// <summary>
/// Register rest client interfaces /// Register rest client interfaces
/// </summary> /// </summary>
@@ -475,8 +442,6 @@ namespace CryptoExchange.Net
services.AddTransient(x => (IFeeRestClient)client(x)!); services.AddTransient(x => (IFeeRestClient)client(x)!);
if (typeof(IBookTickerRestClient).IsAssignableFrom(typeof(T))) if (typeof(IBookTickerRestClient).IsAssignableFrom(typeof(T)))
services.AddTransient(x => (IBookTickerRestClient)client(x)!); services.AddTransient(x => (IBookTickerRestClient)client(x)!);
if (typeof(ITransferRestClient).IsAssignableFrom(typeof(T)))
services.AddTransient(x => (ITransferRestClient)client(x)!);
if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T))) if (typeof(ISpotOrderRestClient).IsAssignableFrom(typeof(T)))
services.AddTransient(x => (ISpotOrderRestClient)client(x)!); services.AddTransient(x => (ISpotOrderRestClient)client(x)!);
@@ -551,6 +516,5 @@ namespace CryptoExchange.Net
return services; return services;
} }
}
} }
@@ -1,16 +1,15 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Time provider
/// </summary>
internal interface IAuthTimeProvider
{ {
/// <summary>
/// Time provider
/// </summary>
internal interface IAuthTimeProvider
{
/// <summary> /// <summary>
/// Get current time /// Get current time
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
DateTime GetTime(); DateTime GetTime();
}
} }
@@ -1,16 +1,15 @@
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base api client
/// </summary>
public interface IBaseApiClient
{ {
/// <summary>
/// Base api client
/// </summary>
public interface IBaseApiClient
{
/// <summary> /// <summary>
/// Base address /// Base address
/// </summary> /// </summary>
@@ -44,5 +43,4 @@ namespace CryptoExchange.Net.Interfaces
/// <typeparam name="T">Api credentials type</typeparam> /// <typeparam name="T">Api credentials type</typeparam>
/// <param name="options">Options to set</param> /// <param name="options">Options to set</param>
void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials; void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials;
}
} }
@@ -1,17 +1,16 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Client for accessing REST API's for different exchanges
/// </summary>
public interface ICryptoRestClient
{ {
/// <summary>
/// Client for accessing REST API's for different exchanges
/// </summary>
public interface ICryptoRestClient
{
/// <summary> /// <summary>
/// Try get /// Try get
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
T TryGet<T>(Func<T> createFunc); T TryGet<T>(Func<T> createFunc);
}
} }
@@ -1,17 +1,16 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Client for accessing Websocket API's for different exchanges
/// </summary>
public interface ICryptoSocketClient
{ {
/// <summary>
/// Client for accessing Websocket API's for different exchanges
/// </summary>
public interface ICryptoSocketClient
{
/// <summary> /// <summary>
/// Try get a client by type for the service collection /// Try get a client by type for the service collection
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
T TryGet<T>(Func<T> createFunc); T TryGet<T>(Func<T> createFunc);
}
} }
@@ -1,13 +0,0 @@
namespace CryptoExchange.Net.Interfaces
{
/// <summary>
/// Service for an exchange
/// </summary>
public interface IExchangeService
{
/// <summary>
/// The exchange the service is for
/// </summary>
public string ExchangeName { get; }
}
}
@@ -1,18 +1,19 @@
using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Message accessor
/// </summary>
public interface IMessageAccessor
{ {
/// <summary>
/// Message accessor
/// </summary>
public interface IMessageAccessor
{
/// <summary> /// <summary>
/// Is this a valid message /// Is this a valid message
/// </summary> /// </summary>
@@ -81,30 +82,29 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
string GetOriginalString(); string GetOriginalString();
} }
/// <summary> /// <summary>
/// Stream message accessor /// Stream message accessor
/// </summary> /// </summary>
public interface IStreamMessageAccessor : IMessageAccessor public interface IStreamMessageAccessor : IMessageAccessor
{ {
/// <summary> /// <summary>
/// Load a stream message /// Load a stream message
/// </summary> /// </summary>
/// <param name="stream"></param> /// <param name="stream"></param>
/// <param name="bufferStream"></param> /// <param name="bufferStream"></param>
Task<CallResult> Read(Stream stream, bool bufferStream); Task<CallResult> Read(Stream stream, bool bufferStream);
} }
/// <summary> /// <summary>
/// Byte message accessor /// Byte message accessor
/// </summary> /// </summary>
public interface IByteMessageAccessor : IMessageAccessor public interface IByteMessageAccessor : IMessageAccessor
{ {
/// <summary> /// <summary>
/// Load a data message /// Load a data message
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
CallResult Read(ReadOnlyMemory<byte> data); CallResult Read(ReadOnlyMemory<byte> data);
}
} }
@@ -1,17 +1,16 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets; using CryptoExchange.Net.Sockets;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Message processor
/// </summary>
public interface IMessageProcessor
{ {
/// <summary>
/// Message processor
/// </summary>
public interface IMessageProcessor
{
/// <summary> /// <summary>
/// Id of the processor /// Id of the processor
/// </summary> /// </summary>
@@ -31,5 +30,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
CallResult<object> Deserialize(IMessageAccessor accessor, Type type); CallResult<object> Deserialize(IMessageAccessor accessor, Type type);
}
} }
@@ -1,37 +1,34 @@
using System.Diagnostics.CodeAnalysis; namespace CryptoExchange.Net.Interfaces;
namespace CryptoExchange.Net.Interfaces /// <summary>
/// Serializer interface
/// </summary>
public interface IMessageSerializer
{ {
/// <summary> }
/// Serializer interface
/// </summary>
public interface IMessageSerializer
{
}
/// <summary> /// <summary>
/// Serialize to byte array /// Serialize to byte array
/// </summary> /// </summary>
public interface IByteMessageSerializer: IMessageSerializer public interface IByteMessageSerializer: IMessageSerializer
{ {
/// <summary> /// <summary>
/// Serialize an object to a string /// Serialize an object to a string
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
byte[] Serialize<T>(T message); byte[] Serialize<T>(T message);
} }
/// <summary> /// <summary>
/// Serialize to string /// Serialize to string
/// </summary> /// </summary>
public interface IStringMessageSerializer: IMessageSerializer public interface IStringMessageSerializer: IMessageSerializer
{ {
/// <summary> /// <summary>
/// Serialize an object to a string /// Serialize an object to a string
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
string Serialize<T>(T message); string Serialize<T>(T message);
}
} }
@@ -1,14 +1,13 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// A provider for a nonce value used when signing requests
/// </summary>
public interface INonceProvider
{ {
/// <summary>
/// A provider for a nonce value used when signing requests
/// </summary>
public interface INonceProvider
{
/// <summary> /// <summary>
/// Get nonce value. Nonce value should be unique and incremental for each call /// Get nonce value. Nonce value should be unique and incremental for each call
/// </summary> /// </summary>
/// <returns>Nonce value</returns> /// <returns>Nonce value</returns>
long GetNonce(); long GetNonce();
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Factory for ISymbolOrderBook instances
/// </summary>
public interface IOrderBookFactory<TOptions> where TOptions : OrderBookOptions
{ {
/// <summary>
/// Factory for ISymbolOrderBook instances
/// </summary>
public interface IOrderBookFactory<TOptions> where TOptions : OrderBookOptions
{
/// <summary> /// <summary>
/// Create a new order book by symbol name /// Create a new order book by symbol name
/// </summary> /// </summary>
@@ -31,5 +31,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="options">Options for the order book</param> /// <param name="options">Options for the order book</param>
/// <returns></returns> /// <returns></returns>
public ISymbolOrderBook Create(SharedSymbol symbol, Action<TOptions>? options = null); public ISymbolOrderBook Create(SharedSymbol symbol, Action<TOptions>? options = null);
}
} }
@@ -4,13 +4,13 @@ using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Rate limiter interface
/// </summary>
public interface IRateLimiter
{ {
/// <summary>
/// Rate limiter interface
/// </summary>
public interface IRateLimiter
{
/// <summary> /// <summary>
/// Limit a request based on previous requests made /// Limit a request based on previous requests made
/// </summary> /// </summary>
@@ -24,5 +24,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="ct">Cancellation token to cancel waiting</param> /// <param name="ct">Cancellation token to cancel waiting</param>
/// <returns>The time in milliseconds spend waiting</returns> /// <returns>The time in milliseconds spend waiting</returns>
Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct); Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct);
}
} }
+7 -12
View File
@@ -1,16 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Request interface
/// </summary>
public interface IRequest
{ {
/// <summary>
/// Request interface
/// </summary>
public interface IRequest
{
/// <summary> /// <summary>
/// Accept header /// Accept header
/// </summary> /// </summary>
@@ -28,10 +28,6 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
Uri Uri { get; } Uri Uri { get; }
/// <summary> /// <summary>
/// HTTP protocol version
/// </summary>
Version HttpVersion { get; }
/// <summary>
/// internal request id for tracing /// internal request id for tracing
/// </summary> /// </summary>
int RequestId { get; } int RequestId { get; }
@@ -66,5 +62,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<IResponse> GetResponseAsync(CancellationToken cancellationToken); Task<IResponse> GetResponseAsync(CancellationToken cancellationToken);
}
} }
@@ -1,33 +1,35 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options;
using System; using System;
using System.Net.Http; using System.Net.Http;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Request factory interface
/// </summary>
public interface IRequestFactory
{ {
/// <summary>
/// Request factory interface
/// </summary>
public interface IRequestFactory
{
/// <summary> /// <summary>
/// Create a request for an uri /// Create a request for an uri
/// </summary> /// </summary>
IRequest Create(Version httpRequestVersion, HttpMethod method, Uri uri, int requestId); /// <param name="method"></param>
/// <param name="uri"></param>
/// <param name="requestId"></param>
/// <returns></returns>
IRequest Create(HttpMethod method, Uri uri, int requestId);
/// <summary> /// <summary>
/// Configure the requests created by this factory /// Configure the requests created by this factory
/// </summary> /// </summary>
/// <param name="options">Rest client options</param> /// <param name="requestTimeout">Request timeout to use</param>
/// <param name="httpClient">Optional shared http client instance</param> /// <param name="httpClient">Optional shared http client instance</param>
void Configure(RestExchangeOptions options, HttpClient? httpClient = null); /// <param name="proxy">Optional proxy to use when no http client is provided</param>
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null);
/// <summary> /// <summary>
/// Update settings /// Update settings
/// </summary> /// </summary>
/// <param name="proxy">Proxy to use</param> /// <param name="proxy">Proxy to use</param>
/// <param name="requestTimeout">Request timeout to use</param> /// <param name="requestTimeout">Request timeout to use</param>
/// <param name="httpKeepAliveInterval">Http client keep alive interval</param> void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout);
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval);
}
} }
+6 -13
View File
@@ -1,26 +1,20 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Response object interface
/// </summary>
public interface IResponse
{ {
/// <summary>
/// Response object interface
/// </summary>
public interface IResponse
{
/// <summary> /// <summary>
/// The response status code /// The response status code
/// </summary> /// </summary>
HttpStatusCode StatusCode { get; } HttpStatusCode StatusCode { get; }
/// <summary>
/// Http protocol version
/// </summary>
Version HttpVersion { get; }
/// <summary> /// <summary>
/// Whether the status code indicates a success status /// Whether the status code indicates a success status
/// </summary> /// </summary>
@@ -46,5 +40,4 @@ namespace CryptoExchange.Net.Interfaces
/// Close the response /// Close the response
/// </summary> /// </summary>
void Close(); void Close();
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base rest API client
/// </summary>
public interface IRestApiClient : IBaseApiClient
{ {
/// <summary>
/// Base rest API client
/// </summary>
public interface IRestApiClient : IBaseApiClient
{
/// <summary> /// <summary>
/// The factory for creating requests. Used for unit testing /// The factory for creating requests. Used for unit testing
/// </summary> /// </summary>
@@ -14,5 +14,4 @@
/// Total amount of requests made with this API client /// Total amount of requests made with this API client
/// </summary> /// </summary>
int TotalRequestsMade { get; set; } int TotalRequestsMade { get; set; }
}
} }
+7 -8
View File
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base class for rest API implementations
/// </summary>
public interface IRestClient: IDisposable
{ {
/// <summary>
/// Base class for rest API implementations
/// </summary>
public interface IRestClient: IDisposable
{
/// <summary> /// <summary>
/// The options provided for this client /// The options provided for this client
/// </summary> /// </summary>
@@ -22,5 +22,4 @@ namespace CryptoExchange.Net.Interfaces
/// The exchange name /// The exchange name
/// </summary> /// </summary>
string Exchange { get; } string Exchange { get; }
}
} }
@@ -1,15 +1,15 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Socket API client
/// </summary>
public interface ISocketApiClient: IBaseApiClient
{ {
/// <summary>
/// Socket API client
/// </summary>
public interface ISocketApiClient: IBaseApiClient
{
/// <summary> /// <summary>
/// The current amount of socket connections on the API client /// The current amount of socket connections on the API client
/// </summary> /// </summary>
@@ -66,5 +66,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<CallResult> PrepareConnectionsAsync(); Task<CallResult> PrepareConnectionsAsync();
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base class for socket API implementations
/// </summary>
public interface ISocketClient: IDisposable
{ {
/// <summary>
/// Base class for socket API implementations
/// </summary>
public interface ISocketClient: IDisposable
{
/// <summary> /// <summary>
/// The exchange name /// The exchange name
/// </summary> /// </summary>
@@ -54,5 +54,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task UnsubscribeAllAsync(); Task UnsubscribeAllAsync();
}
} }
@@ -1,16 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Interface for order book
/// </summary>
public interface ISymbolOrderBook
{ {
/// <summary>
/// Interface for order book
/// </summary>
public interface ISymbolOrderBook
{
/// <summary> /// <summary>
/// The exchange the book is for /// The exchange the book is for
/// </summary> /// </summary>
@@ -127,5 +126,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
string ToString(int rows); string ToString(int rows);
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Interface for order book entries
/// </summary>
public interface ISymbolOrderBookEntry
{ {
/// <summary>
/// Interface for order book entries
/// </summary>
public interface ISymbolOrderBookEntry
{
/// <summary> /// <summary>
/// The quantity of the entry /// The quantity of the entry
/// </summary> /// </summary>
@@ -13,16 +13,15 @@
/// The price of the entry /// The price of the entry
/// </summary> /// </summary>
decimal Price { get; set; } decimal Price { get; set; }
} }
/// <summary> /// <summary>
/// Interface for order book entries /// Interface for order book entries
/// </summary> /// </summary>
public interface ISymbolOrderSequencedBookEntry: ISymbolOrderBookEntry public interface ISymbolOrderSequencedBookEntry: ISymbolOrderBookEntry
{ {
/// <summary> /// <summary>
/// Sequence of the update /// Sequence of the update
/// </summary> /// </summary>
long Sequence { get; set; } long Sequence { get; set; }
}
} }
@@ -1,45 +0,0 @@
using CryptoExchange.Net.SharedApis;
using CryptoExchange.Net.Trackers.Klines;
using CryptoExchange.Net.Trackers.Trades;
using System;
namespace CryptoExchange.Net.Interfaces
{
/// <summary>
/// Tracker factory
/// </summary>
public interface ITrackerFactory
{
/// <summary>
/// Whether the factory supports creating a KlineTracker instance for this symbol and interval
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="interval">The kline interval</param>
bool CanCreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval);
/// <summary>
/// Create a new kline tracker
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="interval">Kline interval</param>
/// <param name="limit">The max amount of klines to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);
/// <summary>
/// Whether the factory supports creating a TradeTracker instance for this symbol
/// </summary>
/// <param name="symbol">The symbol</param>
bool CanCreateTradeTracker(SharedSymbol symbol);
/// <summary>
/// Create a new trade tracker for a symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="limit">The max amount of trades to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
}
}
+7 -8
View File
@@ -1,16 +1,16 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Websocket connection interface
/// </summary>
public interface IWebsocket: IDisposable
{ {
/// <summary>
/// Websocket connection interface
/// </summary>
public interface IWebsocket: IDisposable
{
/// <summary> /// <summary>
/// Websocket closed event /// Websocket closed event
/// </summary> /// </summary>
@@ -106,5 +106,4 @@ namespace CryptoExchange.Net.Interfaces
/// Update proxy setting /// Update proxy setting
/// </summary> /// </summary>
void UpdateProxy(ApiProxy? proxy); void UpdateProxy(ApiProxy? proxy);
}
} }
@@ -1,13 +1,13 @@
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Websocket factory interface
/// </summary>
public interface IWebsocketFactory
{ {
/// <summary>
/// Websocket factory interface
/// </summary>
public interface IWebsocketFactory
{
/// <summary> /// <summary>
/// Create a websocket for an url /// Create a websocket for an url
/// </summary> /// </summary>
@@ -15,5 +15,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="parameters">The parameters to use for the connection</param> /// <param name="parameters">The parameters to use for the connection</param>
/// <returns></returns> /// <returns></returns>
IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters); IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters);
}
} }
+5 -122
View File
@@ -1,62 +1,15 @@
using CryptoExchange.Net.Objects; namespace CryptoExchange.Net;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
namespace CryptoExchange.Net /// <summary>
/// Helpers for client libraries
/// </summary>
public static class LibraryHelpers
{ {
/// <summary>
/// Helpers for client libraries
/// </summary>
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>
@@ -86,74 +39,4 @@ 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>
/// Create a new HttpMessageHandler instance
/// </summary>
public static HttpMessageHandler CreateHttpClientMessageHandler(ApiProxy? proxy, TimeSpan? keepAliveInterval)
{
#if NET5_0_OR_GREATER
var socketHandler = new SocketsHttpHandler();
try
{
if (keepAliveInterval != null && keepAliveInterval != TimeSpan.Zero)
{
socketHandler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always;
socketHandler.KeepAlivePingDelay = keepAliveInterval.Value;
socketHandler.KeepAlivePingTimeout = TimeSpan.FromSeconds(10);
}
socketHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
socketHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
}
catch (PlatformNotSupportedException) { }
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
if (proxy != null)
{
socketHandler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}
return socketHandler;
#else
var httpHandler = new HttpClientHandler();
try
{
httpHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
httpHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
}
catch (PlatformNotSupportedException) { }
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
if (proxy != null)
{
httpHandler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}
return httpHandler;
#endif
}
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class CryptoExchangeWebSocketClientLoggingExtension public static class CryptoExchangeWebSocketClientLoggingExtension
{ {
private static readonly Action<ILogger, int, Exception?> _connecting; private static readonly Action<ILogger, int, Exception?> _connecting;
private static readonly Action<ILogger, int, string, Exception?> _connectionFailed; private static readonly Action<ILogger, int, string, Exception?> _connectionFailed;
private static readonly Action<ILogger, int, Exception?> _connectingCanceled; private static readonly Action<ILogger, int, Exception?> _connectingCanceled;
@@ -383,5 +383,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_connectingCanceled(logger, socketId, null); _connectingCanceled(logger, socketId, null);
} }
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class RateLimitGateLoggingExtensions public static class RateLimitGateLoggingExtensions
{ {
private static readonly Action<ILogger, int, string, string, string, Exception?> _rateLimitRequestFailed; private static readonly Action<ILogger, int, string, string, string, Exception?> _rateLimitRequestFailed;
private static readonly Action<ILogger, int, string, string, Exception?> _rateLimitConnectionFailed; private static readonly Action<ILogger, int, string, string, Exception?> _rateLimitConnectionFailed;
private static readonly Action<ILogger, int, string, TimeSpan, string, string, Exception?> _rateLimitDelayingRequest; private static readonly Action<ILogger, int, string, TimeSpan, string, string, Exception?> _rateLimitDelayingRequest;
@@ -75,5 +75,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_rateLimitAppliedRequest(logger, requestIdId, path, guard, limit, current, null); _rateLimitAppliedRequest(logger, requestIdId, path, guard, limit, current, null);
} }
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class RestApiClientLoggingExtensions public static class RestApiClientLoggingExtensions
{ {
private static readonly Action<ILogger, int?, int?, long, string?, string?, Exception?> _restApiErrorReceived; private static readonly Action<ILogger, int?, int?, long, string?, string?, Exception?> _restApiErrorReceived;
private static readonly Action<ILogger, int?, int?, long, string?, Exception?> _restApiResponseReceived; private static readonly Action<ILogger, int?, int?, long, string?, Exception?> _restApiResponseReceived;
private static readonly Action<ILogger, int, string, Exception?> _restApiFailedToSyncTime; private static readonly Action<ILogger, int, string, Exception?> _restApiFailedToSyncTime;
@@ -155,5 +155,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_restApiCancellationRequested(logger, requestId, null); _restApiCancellationRequested(logger, requestId, null);
} }
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SocketApiClientLoggingExtension public static class SocketApiClientLoggingExtension
{ {
private static readonly Action<ILogger, int, Exception?> _failedToAddSubscriptionRetryOnDifferentConnection; private static readonly Action<ILogger, int, Exception?> _failedToAddSubscriptionRetryOnDifferentConnection;
private static readonly Action<ILogger, int, Exception?> _hasBeenPausedCantSubscribeAtThisMoment; private static readonly Action<ILogger, int, Exception?> _hasBeenPausedCantSubscribeAtThisMoment;
private static readonly Action<ILogger, int, string?, Exception?> _failedToSubscribe; private static readonly Action<ILogger, int, string?, Exception?> _failedToSubscribe;
@@ -23,8 +23,6 @@ namespace CryptoExchange.Net.Logging.Extensions
private static readonly Action<ILogger, int, int, Exception?> _unsubscribingSubscription; private static readonly Action<ILogger, int, int, Exception?> _unsubscribingSubscription;
private static readonly Action<ILogger, int, Exception?> _reconnectingAllConnections; private static readonly Action<ILogger, int, Exception?> _reconnectingAllConnections;
private static readonly Action<ILogger, DateTime, Exception?> _addingRetryAfterGuard; private static readonly Action<ILogger, DateTime, Exception?> _addingRetryAfterGuard;
private static readonly Action<ILogger, Exception?> _timeoutWaitingForReconnectingSocket;
private static readonly Action<ILogger, long, Exception?> _waitedForReconnectingSocket;
static SocketApiClientLoggingExtension() static SocketApiClientLoggingExtension()
{ {
@@ -112,16 +110,6 @@ namespace CryptoExchange.Net.Logging.Extensions
LogLevel.Warning, LogLevel.Warning,
new EventId(3018, "AddRetryAfterGuard"), new EventId(3018, "AddRetryAfterGuard"),
"Adding RetryAfterGuard ({RetryAfter}) because the connection attempt was rate limited"); "Adding RetryAfterGuard ({RetryAfter}) because the connection attempt was rate limited");
_timeoutWaitingForReconnectingSocket = LoggerMessage.Define(
LogLevel.Debug,
new EventId(3019, "TimeoutWaitingForReconnectingSocket"),
"Timeout while waiting for existing socket reconnection, failing request");
_waitedForReconnectingSocket = LoggerMessage.Define<long>(
LogLevel.Trace,
new EventId(3020, "WaitedForReconnectingSocket"),
"Waited for reconnecting socket for {Timespan}ms");
} }
public static void FailedToAddSubscriptionRetryOnDifferentConnection(this ILogger logger, int socketId) public static void FailedToAddSubscriptionRetryOnDifferentConnection(this ILogger logger, int socketId)
@@ -208,14 +196,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_addingRetryAfterGuard(logger, retryAfter, null); _addingRetryAfterGuard(logger, retryAfter, null);
} }
public static void TimeoutWaitingForReconnectingSocket(this ILogger logger)
{
_timeoutWaitingForReconnectingSocket(logger, null);
}
public static void WaitedForReconnectingSocket(this ILogger logger, long milliseconds)
{
_waitedForReconnectingSocket(logger, milliseconds, null);
}
}
} }
@@ -1,18 +1,19 @@
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SocketConnectionLoggingExtension public static class SocketConnectionLoggingExtension
{ {
private static readonly Action<ILogger, int, bool, Exception?> _activityPaused; private static readonly Action<ILogger, int, bool, Exception?> _activityPaused;
private static readonly Action<ILogger, int, Sockets.SocketConnection.SocketStatus, Sockets.SocketConnection.SocketStatus, Exception?> _socketStatusChanged; private static readonly Action<ILogger, int, Sockets.SocketConnection.SocketStatus, Sockets.SocketConnection.SocketStatus, Exception?> _socketStatusChanged;
private static readonly Action<ILogger, int, string?, Exception?> _failedReconnectProcessing; private static readonly Action<ILogger, int, string?, Exception?> _failedReconnectProcessing;
private static readonly Action<ILogger, int, Exception?> _unknownExceptionWhileProcessingReconnection; private static readonly Action<ILogger, int, Exception?> _unknownExceptionWhileProcessingReconnection;
private static readonly Action<ILogger, int, WebSocketError, string?, Exception?> _webSocketErrorCodeAndDetails; private static readonly Action<ILogger, int, WebSocketError, string?, Exception?> _webSocketErrorCodeAndDetails;
private static readonly Action<ILogger, int, string?, Exception?> _webSocketError; private static readonly Action<ILogger, int, string?, Exception?> _webSocketError;
private static readonly Action<ILogger, int, int, Exception?> _messageSentNotPending;
private static readonly Action<ILogger, int, string, Exception?> _receivedData; private static readonly Action<ILogger, int, string, Exception?> _receivedData;
private static readonly Action<ILogger, int, string, Exception?> _failedToParse; private static readonly Action<ILogger, int, string, Exception?> _failedToParse;
private static readonly Action<ILogger, int, string, Exception?> _failedToEvaluateMessage; private static readonly Action<ILogger, int, string, Exception?> _failedToEvaluateMessage;
@@ -71,6 +72,11 @@ namespace CryptoExchange.Net.Logging.Extensions
new EventId(2005, "WebSocketError"), new EventId(2005, "WebSocketError"),
"[Sckt {SocketId}] error: {ErrorMessage}"); "[Sckt {SocketId}] error: {ErrorMessage}");
_messageSentNotPending = LoggerMessage.Define<int, int>(
LogLevel.Debug,
new EventId(2006, "MessageSentNotPending"),
"[Sckt {SocketId}] [Req {RequestId}] message sent, but not pending");
_receivedData = LoggerMessage.Define<int, string>( _receivedData = LoggerMessage.Define<int, string>(
LogLevel.Trace, LogLevel.Trace,
new EventId(2007, "ReceivedData"), new EventId(2007, "ReceivedData"),
@@ -228,6 +234,11 @@ namespace CryptoExchange.Net.Logging.Extensions
_webSocketError(logger, socketId, errorMessage, e); _webSocketError(logger, socketId, errorMessage, e);
} }
public static void MessageSentNotPending(this ILogger logger, int socketId, int requestId)
{
_messageSentNotPending(logger, socketId, requestId, null);
}
public static void ReceivedData(this ILogger logger, int socketId, string originalData) public static void ReceivedData(this ILogger logger, int socketId, string originalData)
{ {
_receivedData(logger, socketId, originalData, null); _receivedData(logger, socketId, originalData, null);
@@ -334,5 +345,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_sendingByteData(logger, socketId, requestId, length, null); _sendingByteData(logger, socketId, requestId, length, null);
} }
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SymbolOrderBookLoggingExtensions public static class SymbolOrderBookLoggingExtensions
{ {
private static readonly Action<ILogger, string, string, OrderBookStatus, OrderBookStatus, Exception?> _orderBookStatusChanged; private static readonly Action<ILogger, string, string, OrderBookStatus, OrderBookStatus, Exception?> _orderBookStatusChanged;
private static readonly Action<ILogger, string, string, Exception?> _orderBookStarting; private static readonly Action<ILogger, string, string, Exception?> _orderBookStarting;
private static readonly Action<ILogger, string, string, Exception?> _orderBookStoppedStarting; private static readonly Action<ILogger, string, string, Exception?> _orderBookStoppedStarting;
@@ -233,5 +233,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_orderBookOutOfSyncChecksum(logger, api, symbol, null); _orderBookOutOfSyncChecksum(logger, api, symbol, null);
} }
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class TrackerLoggingExtensions public static class TrackerLoggingExtensions
{ {
private static readonly Action<ILogger, string, SyncStatus, SyncStatus, Exception?> _klineTrackerStatusChanged; private static readonly Action<ILogger, string, SyncStatus, SyncStatus, Exception?> _klineTrackerStatusChanged;
private static readonly Action<ILogger, string, Exception?> _klineTrackerStarting; private static readonly Action<ILogger, string, Exception?> _klineTrackerStarting;
private static readonly Action<ILogger, string, string, Exception?> _klineTrackerStartFailed; private static readonly Action<ILogger, string, string, Exception?> _klineTrackerStartFailed;
@@ -287,5 +287,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_tradeTrackerConnectionRestored(logger, symbol, null); _tradeTrackerConnectionRestored(logger, symbol, null);
} }
}
} }
+6 -7
View File
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Proxy info
/// </summary>
public class ApiProxy
{ {
/// <summary>
/// Proxy info
/// </summary>
public class ApiProxy
{
/// <summary> /// <summary>
/// The host address of the proxy /// The host address of the proxy
/// </summary> /// </summary>
@@ -38,5 +38,4 @@
Login = login; Login = login;
Password = password; Password = password;
} }
}
} }
+6 -31
View File
@@ -1,18 +1,10 @@
using System; namespace CryptoExchange.Net.Objects;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects /// <summary>
/// An alias used by the exchange for an asset commonly known by another name
/// </summary>
public class AssetAlias
{ {
/// <summary>
/// An alias used by the exchange for an asset commonly known by another name
/// </summary>
public class AssetAlias
{
/// <summary>
/// Alias type
/// </summary>
public AliasType Type { get; set; }
/// <summary> /// <summary>
/// The name of the asset on the exchange /// The name of the asset on the exchange
/// </summary> /// </summary>
@@ -25,26 +17,9 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public AssetAlias(string exchangeName, string commonName, AliasType type = AliasType.BothWays) public AssetAlias(string exchangeName, string commonName)
{ {
ExchangeAssetName = exchangeName; ExchangeAssetName = exchangeName;
CommonAssetName = commonName; CommonAssetName = commonName;
Type = type;
}
}
/// <summary>
/// Alias type
/// </summary>
public enum AliasType
{
/// <summary>
/// Translate both from and to exchange
/// </summary>
BothWays,
/// <summary>
/// Only translate when converting to exchange
/// </summary>
OnlyToExchange
} }
} }
@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Exchange configuration for asset aliases
/// </summary>
public class AssetAliasConfiguration
{ {
/// <summary>
/// Exchange configuration for asset aliases
/// </summary>
public class AssetAliasConfiguration
{
/// <summary> /// <summary>
/// Defined aliases /// Defined aliases
/// </summary> /// </summary>
@@ -23,22 +20,11 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Map the common name to an exchange name for an asset. If there is no alias the input name is returned /// Map the common name to an exchange name for an asset. If there is no alias the input name is returned
/// </summary> /// </summary>
public string CommonToExchangeName(string commonName) => !AutoConvertEnabled ? commonName : Aliases.FirstOrDefault(x => x.CommonAssetName == commonName)?.ExchangeAssetName ?? commonName; public string CommonToExchangeName(string commonName) => !AutoConvertEnabled ? commonName : Aliases.SingleOrDefault(x => x.CommonAssetName == commonName)?.ExchangeAssetName ?? commonName;
/// <summary> /// <summary>
/// Map the exchange name to a common name for an asset. If there is no alias the input name is returned /// Map the exchange name to a common name for an asset. If there is no alias the input name is returned
/// </summary> /// </summary>
public string ExchangeToCommonName(string exchangeName) public string ExchangeToCommonName(string exchangeName) => !AutoConvertEnabled ? exchangeName : Aliases.SingleOrDefault(x => x.ExchangeAssetName == exchangeName)?.CommonAssetName ?? exchangeName;
{
if (!AutoConvertEnabled)
return exchangeName;
var alias = Aliases.FirstOrDefault(x => x.ExchangeAssetName == exchangeName);
if (alias == null || alias.Type == AliasType.OnlyToExchange)
return exchangeName;
return alias.CommonAssetName;
}
}
} }
@@ -1,17 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Async auto reset based on Stephen Toub`s implementation
/// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/
/// </summary>
public class AsyncResetEvent : IDisposable
{ {
/// <summary>
/// Async auto reset based on Stephen Toub`s implementation
/// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/
/// </summary>
public class AsyncResetEvent : IDisposable
{
private static readonly Task<bool> _completed = Task.FromResult(true); private static readonly Task<bool> _completed = Task.FromResult(true);
private Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>(); private Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>();
private bool _signaled; private bool _signaled;
@@ -106,15 +106,28 @@ namespace CryptoExchange.Net.Objects
toRelease.TrySetResult(true); toRelease.TrySetResult(true);
} }
else if (!_signaled) else if (!_signaled)
{
_signaled = true; _signaled = true;
} }
} }
} }
}
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose(bool disposing)
{
if (disposing)
{ {
_waits.Clear(); _waits.Clear();
} }
@@ -1,10 +1,9 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using System; using System;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
internal class AuthTimeProvider : IAuthTimeProvider
{ {
internal class AuthTimeProvider : IAuthTimeProvider
{
public DateTime GetTime() => DateTime.UtcNow; public DateTime GetTime() => DateTime.UtcNow;
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Comparer for byte order
/// </summary>
public class ByteOrderComparer : IComparer<byte[]>
{ {
/// <summary>
/// Comparer for byte order
/// </summary>
public class ByteOrderComparer : IComparer<byte[]>
{
/// <summary> /// <summary>
/// Compare function /// Compare function
/// </summary> /// </summary>
@@ -54,5 +54,4 @@ namespace CryptoExchange.Net.Objects
// Compare lengths. // Compare lengths.
return x.Length < y.Length ? -1 : 1; return x.Length < y.Length ? -1 : 1;
} }
}
} }
+84 -86
View File
@@ -1,4 +1,4 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -6,13 +6,13 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// The result of an operation
/// </summary>
public class CallResult
{ {
/// <summary>
/// The result of an operation
/// </summary>
public class CallResult
{
/// <summary> /// <summary>
/// Static success result /// Static success result
/// </summary> /// </summary>
@@ -51,14 +51,14 @@ namespace CryptoExchange.Net.Objects
{ {
return Success ? $"Success" : $"Error: {Error}"; return Success ? $"Success" : $"Error: {Error}";
} }
} }
/// <summary> /// <summary>
/// The result of an operation /// The result of an operation
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class CallResult<T>: CallResult public class CallResult<T>: CallResult
{ {
/// <summary> /// <summary>
/// The data returned by the call, only available when Success = true /// The data returned by the call, only available when Success = true
/// </summary> /// </summary>
@@ -140,12 +140,12 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> As<K>([AllowNull] K data) public CallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new CallResult<K>(data, OriginalData, Error); return new CallResult<TNew>(data, OriginalData, Error);
} }
/// <summary> /// <summary>
@@ -169,24 +169,24 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the CallResult to a new data type /// Copy the CallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> AsErrorWithData<K>(Error error, K data) public CallResult<TNew> AsErrorWithData<TNew>(Error error, TNew data)
{ {
return new CallResult<K>(data, OriginalData, error); return new CallResult<TNew>(data, OriginalData, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error to return</param> /// <param name="error">The error to return</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> AsError<K>(Error error) public CallResult<TNew> AsError<TNew>(Error error)
{ {
return new CallResult<K>(default, OriginalData, error); return new CallResult<TNew>(default, OriginalData, error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -194,23 +194,18 @@ namespace CryptoExchange.Net.Objects
{ {
return Success ? $"Success" : $"Error: {Error}"; return Success ? $"Success" : $"Error: {Error}";
} }
} }
/// <summary> /// <summary>
/// The result of a request /// The result of a request
/// </summary> /// </summary>
public class WebCallResult : CallResult public class WebCallResult : CallResult
{ {
/// <summary> /// <summary>
/// The request http method /// The request http method
/// </summary> /// </summary>
public HttpMethod? RequestMethod { get; set; } public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary> /// <summary>
/// The headers sent with the request /// The headers sent with the request
/// </summary> /// </summary>
@@ -256,7 +251,6 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
public WebCallResult( public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
Version? httpVersion,
KeyValuePair<string, string[]>[]? responseHeaders, KeyValuePair<string, string[]>[]? responseHeaders,
TimeSpan? responseTime, TimeSpan? responseTime,
string? originalData, string? originalData,
@@ -268,7 +262,6 @@ namespace CryptoExchange.Net.Objects
Error? error) : base(error) Error? error) : base(error)
{ {
ResponseStatusCode = code; ResponseStatusCode = code;
HttpVersion = httpVersion;
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
ResponseTime = responseTime; ResponseTime = responseTime;
RequestId = requestId; RequestId = requestId;
@@ -293,55 +286,55 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public WebCallResult AsError(Error error) public WebCallResult AsError(Error error)
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public WebCallResult<K> As<K>([AllowNull] K data) public WebCallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param> /// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode tradeMode, [AllowNull] TNew data)
{ {
return new ExchangeWebResult<K>(exchange, tradeMode, this.As<K>(data)); return new ExchangeWebResult<TNew>(exchange, tradeMode, this.As<TNew>(data));
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param> /// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode[]? tradeModes, [AllowNull] TNew data)
{ {
return new ExchangeWebResult<K>(exchange, tradeModes, this.As<K>(data)); return new ExchangeWebResult<TNew>(exchange, tradeModes, this.As<TNew>(data));
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public WebCallResult<K> AsError<K>(Error error) public WebCallResult<TNew> AsError<TNew>(Error error)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -349,24 +342,19 @@ namespace CryptoExchange.Net.Objects
{ {
return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}"; return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}";
} }
} }
/// <summary> /// <summary>
/// The result of a request /// The result of a request
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class WebCallResult<T>: CallResult<T> public class WebCallResult<T>: CallResult<T>
{ {
/// <summary> /// <summary>
/// The request http method /// The request http method
/// </summary> /// </summary>
public HttpMethod? RequestMethod { get; set; } public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary> /// <summary>
/// The headers sent with the request /// The headers sent with the request
/// </summary> /// </summary>
@@ -415,9 +403,21 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Create a new result /// Create a new result
/// </summary> /// </summary>
/// <param name="code"></param>
/// <param name="responseHeaders"></param>
/// <param name="responseTime"></param>
/// <param name="responseLength"></param>
/// <param name="originalData"></param>
/// <param name="requestId"></param>
/// <param name="requestUrl"></param>
/// <param name="requestBody"></param>
/// <param name="requestMethod"></param>
/// <param name="requestHeaders"></param>
/// <param name="dataSource"></param>
/// <param name="data"></param>
/// <param name="error"></param>
public WebCallResult( public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
Version? httpVersion,
KeyValuePair<string, string[]>[]? responseHeaders, KeyValuePair<string, string[]>[]? responseHeaders,
TimeSpan? responseTime, TimeSpan? responseTime,
long? responseLength, long? responseLength,
@@ -431,7 +431,6 @@ namespace CryptoExchange.Net.Objects
[AllowNull] T data, [AllowNull] T data,
Error? error) : base(data, originalData, error) Error? error) : base(data, originalData, error)
{ {
HttpVersion = httpVersion;
ResponseStatusCode = code; ResponseStatusCode = code;
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
ResponseTime = responseTime; ResponseTime = responseTime;
@@ -451,7 +450,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult AsDataless() public new WebCallResult AsDataless()
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
} }
/// <summary> /// <summary>
/// Copy as a dataless result /// Copy as a dataless result
@@ -459,47 +458,47 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult AsDatalessError(Error error) public new WebCallResult AsDatalessError(Error error)
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
/// <summary> /// <summary>
/// Create a new error result /// Create a new error result
/// </summary> /// </summary>
/// <param name="error">The error</param> /// <param name="error">The error</param>
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { } public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> As<K>([AllowNull] K data) public new WebCallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> AsError<K>(Error error) public new WebCallResult<TNew> AsError<TNew>(Error error)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> AsErrorWithData<K>(Error error, K data) public new WebCallResult<TNew> AsErrorWithData<TNew>(Error error, TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error);
} }
/// <summary> /// <summary>
@@ -527,41 +526,41 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param> /// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">Data</param> /// <param name="data">Data</param>
/// <param name="nextPageToken">Next page token</param> /// <param name="nextPageToken">Next page token</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data, INextPageToken? nextPageToken = null) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode tradeMode, [AllowNull] TNew data, INextPageToken? nextPageToken = null)
{ {
return new ExchangeWebResult<K>(exchange, tradeMode, As<K>(data), nextPageToken); return new ExchangeWebResult<TNew>(exchange, tradeMode, As<TNew>(data), nextPageToken);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param> /// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">Data</param> /// <param name="data">Data</param>
/// <param name="nextPageToken">Next page token</param> /// <param name="nextPageToken">Next page token</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data, INextPageToken? nextPageToken = null) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode[]? tradeModes, [AllowNull] TNew data, INextPageToken? nextPageToken = null)
{ {
return new ExchangeWebResult<K>(exchange, tradeModes, As<K>(data), nextPageToken); return new ExchangeWebResult<TNew>(exchange, tradeModes, As<TNew>(data), nextPageToken);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult with a specific error /// Copy the WebCallResult to an ExchangeWebResult with a specific error
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeError<K>(string exchange, Error error) public ExchangeWebResult<TNew> AsExchangeError<TNew>(string exchange, Error error)
{ {
return new ExchangeWebResult<K>(exchange, null, AsError<K>(error)); return new ExchangeWebResult<TNew>(exchange, null, AsError<TNew>(error));
} }
/// <summary> /// <summary>
@@ -570,7 +569,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
internal WebCallResult<T> Cached() internal WebCallResult<T> Cached()
{ {
return new WebCallResult<T>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error); return new WebCallResult<T>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -585,5 +584,4 @@ namespace CryptoExchange.Net.Objects
return sb.ToString(); return sb.ToString();
} }
}
} }
+6 -7
View File
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Constants
/// </summary>
public class Constants
{ {
/// <summary>
/// Constants
/// </summary>
public class Constants
{
/// <summary> /// <summary>
/// Json content type header /// Json content type header
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Placeholder key for when request body should be set to the value of this KVP /// Placeholder key for when request body should be set to the value of this KVP
/// </summary> /// </summary>
public const string BodyPlaceHolderKey = "_BODY_"; public const string BodyPlaceHolderKey = "_BODY_";
}
} }
+83 -114
View File
@@ -1,12 +1,10 @@
using CryptoExchange.Net.Attributes; namespace CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.Objects /// <summary>
/// What to do when a request would exceed the rate limit
/// </summary>
public enum RateLimitingBehaviour
{ {
/// <summary>
/// What to do when a request would exceed the rate limit
/// </summary>
public enum RateLimitingBehaviour
{
/// <summary> /// <summary>
/// Fail the request /// Fail the request
/// </summary> /// </summary>
@@ -15,13 +13,13 @@ namespace CryptoExchange.Net.Objects
/// Wait till the request can be send /// Wait till the request can be send
/// </summary> /// </summary>
Wait Wait
} }
/// <summary> /// <summary>
/// What to do when a request would exceed the rate limit /// What to do when a request would exceed the rate limit
/// </summary> /// </summary>
public enum RateLimitWindowType public enum RateLimitWindowType
{ {
/// <summary> /// <summary>
/// A sliding window /// A sliding window
/// </summary> /// </summary>
@@ -38,13 +36,13 @@ namespace CryptoExchange.Net.Objects
/// Decaying window /// Decaying window
/// </summary> /// </summary>
Decay Decay
} }
/// <summary> /// <summary>
/// Where the parameters for a HttpMethod should be added in a request /// Where the parameters for a HttpMethod should be added in a request
/// </summary> /// </summary>
public enum HttpMethodParameterPosition public enum HttpMethodParameterPosition
{ {
/// <summary> /// <summary>
/// Parameters in body /// Parameters in body
/// </summary> /// </summary>
@@ -53,13 +51,13 @@ namespace CryptoExchange.Net.Objects
/// Parameters in url /// Parameters in url
/// </summary> /// </summary>
InUri InUri
} }
/// <summary> /// <summary>
/// The format of the request body /// The format of the request body
/// </summary> /// </summary>
public enum RequestBodyFormat public enum RequestBodyFormat
{ {
/// <summary> /// <summary>
/// Form data /// Form data
/// </summary> /// </summary>
@@ -68,13 +66,13 @@ namespace CryptoExchange.Net.Objects
/// Json /// Json
/// </summary> /// </summary>
Json Json
} }
/// <summary> /// <summary>
/// Tracker sync status /// Tracker sync status
/// </summary> /// </summary>
public enum SyncStatus public enum SyncStatus
{ {
/// <summary> /// <summary>
/// Not connected /// Not connected
/// </summary> /// </summary>
@@ -95,13 +93,13 @@ namespace CryptoExchange.Net.Objects
/// Disposed /// Disposed
/// </summary> /// </summary>
Disposed Disposed
} }
/// <summary> /// <summary>
/// Status of the order book /// Status of the order book
/// </summary> /// </summary>
public enum OrderBookStatus public enum OrderBookStatus
{ {
/// <summary> /// <summary>
/// Not connected /// Not connected
/// </summary> /// </summary>
@@ -130,13 +128,13 @@ namespace CryptoExchange.Net.Objects
/// Disposed /// Disposed
/// </summary> /// </summary>
Disposed Disposed
} }
/// <summary> /// <summary>
/// Order book entry type /// Order book entry type
/// </summary> /// </summary>
public enum OrderBookEntryType public enum OrderBookEntryType
{ {
/// <summary> /// <summary>
/// Ask /// Ask
/// </summary> /// </summary>
@@ -145,14 +143,14 @@ namespace CryptoExchange.Net.Objects
/// Bid /// Bid
/// </summary> /// </summary>
Bid Bid
} }
/// <summary> /// <summary>
/// Define how array parameters should be send /// Define how array parameters should be send
/// </summary> /// </summary>
public enum ArrayParametersSerialization public enum ArrayParametersSerialization
#pragma warning disable CS1570 // XML comment has badly formed XML #pragma warning disable CS1570 // XML comment has badly formed XML
{ {
/// <summary> /// <summary>
/// Send as key=value1&key=value2 /// Send as key=value1&key=value2
/// </summary> /// </summary>
@@ -167,13 +165,13 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
JsonArray JsonArray
#pragma warning restore CS1570 // XML comment has badly formed XML #pragma warning restore CS1570 // XML comment has badly formed XML
} }
/// <summary> /// <summary>
/// How to round /// How to round
/// </summary> /// </summary>
public enum RoundingType public enum RoundingType
{ {
/// <summary> /// <summary>
/// Round down (flooring) /// Round down (flooring)
/// </summary> /// </summary>
@@ -186,13 +184,13 @@ namespace CryptoExchange.Net.Objects
/// Round up (ceil) /// Round up (ceil)
/// </summary> /// </summary>
Up Up
} }
/// <summary> /// <summary>
/// Type of the update /// Type of the update
/// </summary> /// </summary>
public enum SocketUpdateType public enum SocketUpdateType
{ {
/// <summary> /// <summary>
/// A update /// A update
/// </summary> /// </summary>
@@ -201,13 +199,13 @@ namespace CryptoExchange.Net.Objects
/// A snapshot, generally send at the start of the connection /// A snapshot, generally send at the start of the connection
/// </summary> /// </summary>
Snapshot Snapshot
} }
/// <summary> /// <summary>
/// Reconnect policy /// Reconnect policy
/// </summary> /// </summary>
public enum ReconnectPolicy public enum ReconnectPolicy
{ {
/// <summary> /// <summary>
/// Reconnect is disabled /// Reconnect is disabled
/// </summary> /// </summary>
@@ -220,13 +218,13 @@ namespace CryptoExchange.Net.Objects
/// Backoff policy of 2^`reconnectAttempt`, where `reconnectAttempt` has a max value of 5 /// Backoff policy of 2^`reconnectAttempt`, where `reconnectAttempt` has a max value of 5
/// </summary> /// </summary>
ExponentialBackoff ExponentialBackoff
} }
/// <summary> /// <summary>
/// The data source of the result /// The data source of the result
/// </summary> /// </summary>
public enum ResultDataSource public enum ResultDataSource
{ {
/// <summary> /// <summary>
/// From server /// From server
/// </summary> /// </summary>
@@ -235,13 +233,13 @@ namespace CryptoExchange.Net.Objects
/// From cache /// From cache
/// </summary> /// </summary>
Cache Cache
} }
/// <summary> /// <summary>
/// Type of exchange /// Type of exchange
/// </summary> /// </summary>
public enum ExchangeType public enum ExchangeType
{ {
/// <summary> /// <summary>
/// Centralized /// Centralized
/// </summary> /// </summary>
@@ -250,13 +248,13 @@ namespace CryptoExchange.Net.Objects
/// Decentralized /// Decentralized
/// </summary> /// </summary>
DEX DEX
} }
/// <summary> /// <summary>
/// Timeout behavior for queries /// Timeout behavior for queries
/// </summary> /// </summary>
public enum TimeoutBehavior public enum TimeoutBehavior
{ {
/// <summary> /// <summary>
/// Fail the request /// Fail the request
/// </summary> /// </summary>
@@ -265,33 +263,4 @@ namespace CryptoExchange.Net.Objects
/// Mark the query as successful /// Mark the query as successful
/// </summary> /// </summary>
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
}
} }
+102 -104
View File
@@ -1,14 +1,13 @@
using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Errors;
using System; using System;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
{
/// <summary>
/// Base class for errors
/// </summary>
public abstract class Error
{
/// <summary>
/// Base class for errors
/// </summary>
public abstract class Error
{
private int? _code; private int? _code;
/// <summary> /// <summary>
/// The int error code the server returned; or the http status code int value if there was no error code.<br /> /// The int error code the server returned; or the http status code int value if there was no error code.<br />
@@ -81,60 +80,60 @@ namespace CryptoExchange.Net.Objects
{ {
return ErrorCode != null ? $"[{GetType().Name}.{ErrorType}] {ErrorCode}: {Message ?? ErrorDescription}" : $"[{GetType().Name}.{ErrorType}] {Message ?? ErrorDescription}"; return ErrorCode != null ? $"[{GetType().Name}.{ErrorType}] {ErrorCode}: {Message ?? ErrorDescription}" : $"[{GetType().Name}.{ErrorType}] {Message ?? ErrorDescription}";
} }
} }
/// <summary> /// <summary>
/// Cant reach server error /// Cant reach server error
/// </summary> /// </summary>
public class CantConnectError : Error public class CantConnectError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CantConnectError() : base(null, _errorInfo, null) { } public CantConnectError() : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CantConnectError(Exception? exception) : base(null, _errorInfo, exception) { } public CantConnectError(Exception? exception) : base(null, errorInfo, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// No api credentials provided while trying to access a private endpoint /// No api credentials provided while trying to access a private endpoint
/// </summary> /// </summary>
public class NoApiCredentialsError : Error public class NoApiCredentialsError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "No credentials provided for private endpoint"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "No credentials provided for private endpoint");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public NoApiCredentialsError() : base(null, _errorInfo, null) { } public NoApiCredentialsError() : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Error returned by the server /// Error returned by the server
/// </summary> /// </summary>
public class ServerError : Error public class ServerError : Error
{ {
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -151,91 +150,91 @@ namespace CryptoExchange.Net.Objects
/// ctor /// ctor
/// </summary> /// </summary>
public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { } public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { }
} }
/// <summary> /// <summary>
/// Web error returned by the server /// Web error returned by the server
/// </summary> /// </summary>
public class WebError : Error public class WebError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.NetworkError, true, "Failed to complete the request to the server due to a network error"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.NetworkError, true, "Failed to complete the request to the server due to a network error");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public WebError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public WebError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// Timeout error waiting for a response from the server /// Timeout error waiting for a response from the server
/// </summary> /// </summary>
public class TimeoutError : Error public class TimeoutError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public TimeoutError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public TimeoutError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// Error while deserializing data /// Error while deserializing data
/// </summary> /// </summary>
public class DeserializeError : Error public class DeserializeError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public DeserializeError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public DeserializeError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// An invalid parameter has been provided /// An invalid parameter has been provided
/// </summary> /// </summary>
public class ArgumentError : Error public class ArgumentError : Error
{ {
/// <summary> /// <summary>
/// Default error info for missing parameter /// Default error info for missing parameter
/// </summary> /// </summary>
protected static readonly ErrorInfo _missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter"); protected static readonly ErrorInfo missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter");
/// <summary> /// <summary>
/// Default error info for invalid parameter /// Default error info for invalid parameter
/// </summary> /// </summary>
protected static readonly ErrorInfo _invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter"); protected static readonly ErrorInfo invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public static ArgumentError Missing(string parameterName, string? message = null) => new ArgumentError(_missingInfo with { Message = message == null ? $"{_missingInfo.Message} '{parameterName}'" : $"{_missingInfo.Message} '{parameterName}': {message}" }, null); public static ArgumentError Missing(string parameterName, string? message = null) => new ArgumentError(missingInfo with { Message = message == null ? $"{missingInfo.Message} '{parameterName}'" : $"{missingInfo.Message} '{parameterName}': {message}" }, null);
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(_invalidInfo with { Message = $"{_invalidInfo.Message} '{parameterName}': {message}" }, null); public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(invalidInfo with { Message = $"{invalidInfo.Message} '{parameterName}': {message}" }, null);
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (client side) /// Rate limit exceeded (client side)
/// </summary> /// </summary>
public abstract class BaseRateLimitError : Error public abstract class BaseRateLimitError : Error
{ {
/// <summary> /// <summary>
/// When the request can be retried /// When the request can be retried
/// </summary> /// </summary>
@@ -245,89 +244,88 @@ namespace CryptoExchange.Net.Objects
/// ctor /// ctor
/// </summary> /// </summary>
protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { } protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (client side) /// Rate limit exceeded (client side)
/// </summary> /// </summary>
public class ClientRateLimitError : BaseRateLimitError public class ClientRateLimitError : BaseRateLimitError
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Client rate limit exceeded"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Client rate limit exceeded");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public ClientRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public ClientRateLimitError(string? message = null, Exception? exception = null) : base(errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { } protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (server side) /// Rate limit exceeded (server side)
/// </summary> /// </summary>
public class ServerRateLimitError : BaseRateLimitError public class ServerRateLimitError : BaseRateLimitError
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Server rate limit exceeded"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Server rate limit exceeded");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public ServerRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public ServerRateLimitError(string? message = null, Exception? exception = null) : base(errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { } protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
} }
/// <summary> /// <summary>
/// Cancellation requested /// Cancellation requested
/// </summary> /// </summary>
public class CancellationRequestedError : Error public class CancellationRequestedError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.CancellationRequested, false, "Cancellation requested"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.CancellationRequested, false, "Cancellation requested");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CancellationRequestedError(Exception? exception = null) : base(null, _errorInfo, null) { } public CancellationRequestedError(Exception? exception = null) : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Invalid operation requested /// Invalid operation requested
/// </summary> /// </summary>
public class InvalidOperationError : Error public class InvalidOperationError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "Operation invalid"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "Operation invalid");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public InvalidOperationError(string message) : base(null, _errorInfo with { Message = message }, null) { } public InvalidOperationError(string message) : base(null, errorInfo with { Message = message }, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
} }
@@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects.Errors namespace CryptoExchange.Net.Objects.Errors;
/// <summary>
/// Error evaluator
/// </summary>
public class ErrorEvaluator
{ {
/// <summary>
/// Error evaluator
/// </summary>
public class ErrorEvaluator
{
/// <summary> /// <summary>
/// Error code /// Error code
/// </summary> /// </summary>
@@ -36,5 +34,4 @@ namespace CryptoExchange.Net.Objects.Errors
ErrorCodes = errorCodes; ErrorCodes = errorCodes;
ErrorTypeEvaluator = errorTypeEvaluator; ErrorTypeEvaluator = errorTypeEvaluator;
} }
}
} }

Some files were not shown because too many files have changed in this diff Show More