From 2c7f85ae5a86eaa0e2b6e62804b58655470b7d3b Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 18 Nov 2024 14:39:50 +0100 Subject: [PATCH 1/2] wip --- Kraken.Net.UnitTests/KrakenClientTests.cs | 102 +++++++++++++++ .../KrakenRestIntegrationTests.cs | 10 +- Kraken.Net/Clients/KrakenRestClient.cs | 21 ++-- Kraken.Net/Clients/KrakenSocketClient.cs | 28 ++--- .../ServiceCollectionExtensions.cs | 116 ++++++++++++++---- Kraken.Net/Kraken.Net.csproj | 4 +- Kraken.Net/Kraken.Net.xml | 85 ++++++++++--- Kraken.Net/KrakenEnvironment.cs | 20 +++ Kraken.Net/Objects/Options/KrakenOptions.cs | 38 ++++++ .../Objects/Options/KrakenRestOptions.cs | 24 ++-- .../Objects/Options/KrakenSocketOptions.cs | 22 ++-- 11 files changed, 379 insertions(+), 91 deletions(-) create mode 100644 Kraken.Net/Objects/Options/KrakenOptions.cs diff --git a/Kraken.Net.UnitTests/KrakenClientTests.cs b/Kraken.Net.UnitTests/KrakenClientTests.cs index fae2991..0e985e7 100644 --- a/Kraken.Net.UnitTests/KrakenClientTests.cs +++ b/Kraken.Net.UnitTests/KrakenClientTests.cs @@ -11,6 +11,10 @@ using System.Net.Http; using CryptoExchange.Net.Testing.Implementations; using System.Text.Json; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using CryptoExchange.Net.Objects; +using Kraken.Net.Interfaces.Clients; namespace Kraken.Net.UnitTests { @@ -170,5 +174,103 @@ public void CheckInterfaces() CryptoExchange.Net.Testing.TestHelpers.CheckForMissingRestInterfaces(); CryptoExchange.Net.Testing.TestHelpers.CheckForMissingSocketInterfaces(); } + + [Test] + [TestCase(TradeEnvironmentNames.Live, "https://api.kraken.com")] + [TestCase("", "https://api.kraken.com")] + public void TestConstructorEnvironments(string environmentName, string expected) + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Kraken:Environment:Name", environmentName }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddKraken(configuration.GetSection("Kraken")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.SpotApi.BaseAddress; + + Assert.That(address, Is.EqualTo(expected)); + } + + [Test] + public void TestConstructorNullEnvironment() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Kraken", null }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddKraken(configuration.GetSection("Kraken")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.SpotApi.BaseAddress; + + Assert.That(address, Is.EqualTo("https://api.kraken.com")); + } + + [Test] + public void TestConstructorApiOverwriteEnvironment() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Kraken:Environment:Name", "test" }, + { "Kraken:Rest:Environment:Name", "live" }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddKraken(configuration.GetSection("Kraken")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.SpotApi.BaseAddress; + + Assert.That(address, Is.EqualTo("https://api.kraken.com")); + } + + [Test] + public void TestConstructorConfiguration() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "ApiCredentials:Key", "123" }, + { "ApiCredentials:Secret", "4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==" }, + { "Socket:ApiCredentials:Key", "456" }, + { "Socket:ApiCredentials:Secret", "4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==" }, + { "Rest:OutputOriginalData", "true" }, + { "Socket:OutputOriginalData", "false" }, + { "Rest:Proxy:Host", "host" }, + { "Rest:Proxy:Port", "80" }, + { "Socket:Proxy:Host", "host2" }, + { "Socket:Proxy:Port", "81" }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddKraken(configuration); + var provider = collection.BuildServiceProvider(); + + var restClient = provider.GetRequiredService(); + var socketClient = provider.GetRequiredService(); + + Assert.That(((BaseApiClient)restClient.SpotApi).OutputOriginalData, Is.True); + Assert.That(((BaseApiClient)socketClient.SpotApi).OutputOriginalData, Is.False); + Assert.That(((BaseApiClient)restClient.SpotApi).AuthenticationProvider.ApiKey, Is.EqualTo("123")); + Assert.That(((BaseApiClient)socketClient.SpotApi).AuthenticationProvider.ApiKey, Is.EqualTo("456")); + Assert.That(((BaseApiClient)restClient.SpotApi).ClientOptions.Proxy.Host, Is.EqualTo("host")); + Assert.That(((BaseApiClient)restClient.SpotApi).ClientOptions.Proxy.Port, Is.EqualTo(80)); + Assert.That(((BaseApiClient)socketClient.SpotApi).ClientOptions.Proxy.Host, Is.EqualTo("host2")); + Assert.That(((BaseApiClient)socketClient.SpotApi).ClientOptions.Proxy.Port, Is.EqualTo(81)); + } } } diff --git a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs index aac5c81..005566d 100644 --- a/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs +++ b/Kraken.Net.UnitTests/KrakenRestIntegrationTests.cs @@ -5,6 +5,8 @@ using NUnit.Framework; using System; using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Kraken.Net.Objects.Options; namespace Kraken.Net.UnitTests { @@ -23,11 +25,11 @@ public override KrakenRestClient GetClient(ILoggerFactory loggerFactory) var sec = Environment.GetEnvironmentVariable("APISECRET"); Authenticated = key != null && sec != null; - return new KrakenRestClient(null, loggerFactory, opts => + return new KrakenRestClient(null, loggerFactory, Options.Create(new KrakenRestOptions { - opts.OutputOriginalData = true; - opts.ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null; - }); + OutputOriginalData = true, + ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null + })); } [Test] diff --git a/Kraken.Net/Clients/KrakenRestClient.cs b/Kraken.Net/Clients/KrakenRestClient.cs index e91265a..7098750 100644 --- a/Kraken.Net/Clients/KrakenRestClient.cs +++ b/Kraken.Net/Clients/KrakenRestClient.cs @@ -5,6 +5,7 @@ using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects.Options; +using Microsoft.Extensions.Options; namespace Kraken.Net.Clients { @@ -26,26 +27,24 @@ public class KrakenRestClient : BaseRestClient, IKrakenRestClient /// Create a new instance of the KrakenRestClient using provided options /// /// Option configuration delegate - public KrakenRestClient(Action? optionsDelegate = null) : this(null, null, optionsDelegate) + public KrakenRestClient(Action? optionsDelegate = null) + : this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate))) { } /// /// Create a new instance of the KrakenRestClient /// - /// Option configuration delegate + /// Option configuration /// The logger factory /// Http client for this client - public KrakenRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Action? optionsDelegate = null) + public KrakenRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, IOptions options) : base(loggerFactory, "Kraken") { - var options = KrakenRestOptions.Default.Copy(); - if (optionsDelegate != null) - optionsDelegate(options); - Initialize(options); + Initialize(options.Value); - SpotApi = AddApiClient(new KrakenRestClientSpotApi(_logger, httpClient, options)); - FuturesApi = AddApiClient(new KrakenRestClientFuturesApi(_logger, httpClient, options)); + SpotApi = AddApiClient(new KrakenRestClientSpotApi(_logger, httpClient, options.Value)); + FuturesApi = AddApiClient(new KrakenRestClientFuturesApi(_logger, httpClient, options.Value)); } #endregion @@ -57,9 +56,7 @@ public KrakenRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, A /// Option configuration delegate public static void SetDefaultOptions(Action optionsDelegate) { - var options = KrakenRestOptions.Default.Copy(); - optionsDelegate(options); - KrakenRestOptions.Default = options; + KrakenRestOptions.Default = ApplyOptionsDelegate(optionsDelegate); } /// diff --git a/Kraken.Net/Clients/KrakenSocketClient.cs b/Kraken.Net/Clients/KrakenSocketClient.cs index 26a24e0..67372ae 100644 --- a/Kraken.Net/Clients/KrakenSocketClient.cs +++ b/Kraken.Net/Clients/KrakenSocketClient.cs @@ -5,6 +5,7 @@ using Kraken.Net.Interfaces.Clients.FuturesApi; using Kraken.Net.Interfaces.Clients.SpotApi; using Kraken.Net.Objects.Options; +using Microsoft.Extensions.Options; namespace Kraken.Net.Clients { @@ -21,19 +22,12 @@ public class KrakenSocketClient : BaseSocketClient, IKrakenSocketClient #endregion #region ctor - /// - /// Create a new instance of the KrakenSocketClient - /// - /// The logger - public KrakenSocketClient(ILoggerFactory? loggerFactory = null) : this((x) => { }, loggerFactory) - { - } - /// /// Create a new instance of the KrakenSocketClient /// /// Option configuration delegate - public KrakenSocketClient(Action optionsDelegate) : this(optionsDelegate, null) + public KrakenSocketClient(Action? optionsDelegate = null) + : this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null) { } @@ -41,15 +35,13 @@ public KrakenSocketClient(Action optionsDelegate) : this(op /// Create a new instance of the KrakenSocketClient /// /// The logger - /// Option configuration delegate - public KrakenSocketClient(Action optionsDelegate, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Kraken") + /// Option configuration + public KrakenSocketClient(IOptions options, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Kraken") { - var options = KrakenSocketOptions.Default.Copy(); - optionsDelegate(options); - Initialize(options); + Initialize(options.Value); - SpotApi = AddApiClient(new KrakenSocketClientSpotApi(_logger, options)); - FuturesApi = AddApiClient(new KrakenSocketClientFuturesApi(_logger, options)); + SpotApi = AddApiClient(new KrakenSocketClientSpotApi(_logger, options.Value)); + FuturesApi = AddApiClient(new KrakenSocketClientFuturesApi(_logger, options.Value)); } #endregion @@ -61,9 +53,7 @@ public KrakenSocketClient(Action optionsDelegate, ILoggerFa /// Option configuration delegate public static void SetDefaultOptions(Action optionsDelegate) { - var options = KrakenSocketOptions.Default.Copy(); - optionsDelegate(options); - KrakenSocketOptions.Default = options; + KrakenSocketOptions.Default = ApplyOptionsDelegate(optionsDelegate); } /// diff --git a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs index bc8f627..4d6aff4 100644 --- a/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/Kraken.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -5,6 +5,8 @@ using Kraken.Net.Interfaces.Clients; using Kraken.Net.Objects.Options; using Kraken.Net.SymbolOrderBooks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; using System.Net; namespace Microsoft.Extensions.DependencyInjection @@ -14,46 +16,114 @@ namespace Microsoft.Extensions.DependencyInjection /// public static class ServiceCollectionExtensions { + /// - /// Add the IKrakenClient and IKrakenSocketClient to the sevice collection so they can be injected + /// Add services such as the IKrakenRestClient and IKrakenSocketClient. Configures the services based on the provided configuration. /// /// The service collection - /// Set default options for the rest client - /// Set default options for the socket client - /// The lifetime of the IKrakenSocketClient for the service collection. Defaults to Singleton. + /// The configuration(section) containing the options /// public static IServiceCollection AddKraken( this IServiceCollection services, - Action? defaultRestOptionsDelegate = null, - Action? defaultSocketOptionsDelegate = null, - ServiceLifetime? socketClientLifeTime = null) + IConfiguration configuration) { - var restOptions = KrakenRestOptions.Default.Copy(); + var options = new KrakenOptions(); + // Reset environment so we know if theyre overriden + options.Rest.Environment = null!; + options.Socket.Environment = null!; + configuration.Bind(options); - if (defaultRestOptionsDelegate != null) - { - defaultRestOptionsDelegate(restOptions); - KrakenRestClient.SetDefaultOptions(defaultRestOptionsDelegate); - } + if (options.Rest == null || options.Socket == null) + throw new ArgumentException("Options null"); + + var restEnvName = options.Rest.Environment?.Name ?? options.Environment?.Name ?? KrakenEnvironment.Live.Name; + var socketEnvName = options.Socket.Environment?.Name ?? options.Environment?.Name ?? KrakenEnvironment.Live.Name; + options.Rest.Environment = KrakenEnvironment.GetEnvironmentByName(restEnvName) ?? options.Rest.Environment!; + options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials; + options.Socket.Environment = KrakenEnvironment.GetEnvironmentByName(socketEnvName) ?? options.Socket.Environment!; + options.Socket.ApiCredentials = options.Socket.ApiCredentials ?? options.ApiCredentials; + + + services.AddSingleton(x => Options.Options.Create(options.Rest)); + services.AddSingleton(x => Options.Options.Create(options.Socket)); + + return AddKrakenCore(services, options.SocketClientLifeTime); + } + + /// + /// Add services such as the IKrakenRestClient and IKrakenSocketClient. Services will be configured based on the provided options. + /// + /// The service collection + /// Set options for the Kraken services + /// + public static IServiceCollection AddKraken( + this IServiceCollection services, + Action? optionsDelegate = null) + { + var options = new KrakenOptions(); + // Reset environment so we know if theyre overriden + options.Rest.Environment = null!; + options.Socket.Environment = null!; + optionsDelegate?.Invoke(options); + if (options.Rest == null || options.Socket == null) + throw new ArgumentException("Options null"); + + options.Rest.Environment = options.Rest.Environment ?? options.Environment ?? KrakenEnvironment.Live; + options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials; + options.Socket.Environment = options.Socket.Environment ?? options.Environment ?? KrakenEnvironment.Live; + options.Socket.ApiCredentials = options.Socket.ApiCredentials ?? options.ApiCredentials; - if (defaultSocketOptionsDelegate != null) - KrakenSocketClient.SetDefaultOptions(defaultSocketOptionsDelegate); + services.AddSingleton(x => Options.Options.Create(options.Rest)); + services.AddSingleton(x => Options.Options.Create(options.Socket)); - services.AddHttpClient(options => + return AddKrakenCore(services, options.SocketClientLifeTime); + } + + /// + /// DEPRECATED; use instead + /// + public static IServiceCollection AddKraken( + this IServiceCollection services, + Action restDelegate, + Action? socketDelegate = null, + ServiceLifetime? socketClientLifeTime = null) + { + services.Configure((x) => { restDelegate?.Invoke(x); }); + services.Configure((x) => { socketDelegate?.Invoke(x); }); + + return AddKrakenCore(services, socketClientLifeTime); + } + + private static IServiceCollection AddKrakenCore( + this IServiceCollection services, + ServiceLifetime? socketClientLifeTime = null) + { + services.AddHttpClient((client, serviceProvider) => { - options.Timeout = restOptions.RequestTimeout; - }).ConfigurePrimaryHttpMessageHandler(() => { + var options = serviceProvider.GetRequiredService>().Value; + client.Timeout = options.RequestTimeout; + return new KrakenRestClient(client, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService>()); + }).ConfigurePrimaryHttpMessageHandler((serviceProvider) => { var handler = new HttpClientHandler(); - if (restOptions.Proxy != null) + try + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + catch (PlatformNotSupportedException) + { } + + var options = serviceProvider.GetRequiredService>().Value; + if (options.Proxy != null) { handler.Proxy = new WebProxy { - Address = new Uri($"{restOptions.Proxy.Host}:{restOptions.Proxy.Port}"), - Credentials = restOptions.Proxy.Password == null ? null : new NetworkCredential(restOptions.Proxy.Login, restOptions.Proxy.Password) + Address = new Uri($"{options.Proxy.Host}:{options.Proxy.Port}"), + Credentials = options.Proxy.Password == null ? null : new NetworkCredential(options.Proxy.Login, options.Proxy.Password) }; } return handler; }); + services.Add(new ServiceDescriptor(typeof(IKrakenSocketClient), x => { return new KrakenSocketClient(x.GetRequiredService>(), x.GetRequiredService()); }, socketClientLifeTime ?? ServiceLifetime.Singleton)); services.AddTransient(); services.AddTransient(); @@ -66,10 +136,6 @@ public static IServiceCollection AddKraken( services.RegisterSharedRestInterfaces(x => x.GetRequiredService().FuturesApi.SharedClient); services.RegisterSharedSocketInterfaces(x => x.GetRequiredService().FuturesApi.SharedClient); - if (socketClientLifeTime == null) - services.AddSingleton(); - else - services.Add(new ServiceDescriptor(typeof(IKrakenSocketClient), typeof(KrakenSocketClient), socketClientLifeTime.Value)); return services; } } diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj index 196f8cc..7bf8807 100644 --- a/Kraken.Net/Kraken.Net.csproj +++ b/Kraken.Net/Kraken.Net.csproj @@ -51,10 +51,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml index 73d0ff0..9e6138e 100644 --- a/Kraken.Net/Kraken.Net.xml +++ b/Kraken.Net/Kraken.Net.xml @@ -229,11 +229,11 @@ Option configuration delegate - + Create a new instance of the KrakenRestClient - Option configuration delegate + Option configuration The logger factory Http client for this client @@ -255,24 +255,18 @@ - - - Create a new instance of the KrakenSocketClient - - The logger - Create a new instance of the KrakenSocketClient Option configuration delegate - + Create a new instance of the KrakenSocketClient The logger - Option configuration delegate + Option configuration @@ -3698,6 +3692,16 @@ Futures Socket client address + + + ctor for DI, use for creating a custom environment + + + + + Get the Kraken environment by name + + Live environment @@ -9465,6 +9469,36 @@ Expires after x seconds + + + Kraken options + + + + + Rest client options + + + + + Socket client options + + + + + Trade environment. Contains info about URL's to use to connect to the API. Use `KrakenEnvironment` to swap environment, for example `Environment = KrakenEnvironment.Live` + + + + + The api credentials used for signing requests. + + + + + The DI service lifetime for the IKrakenSocketClient + + Options for the Kraken SymbolOrderBook @@ -9495,6 +9529,11 @@ Default options for new KrakenRestClients + + + ctor + + The static password configured as two-factor authentication for the API key. Will be send as otp parameter on private requests. @@ -9525,6 +9564,11 @@ Default options for new KrakenRestClients + + + ctor + + Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that @@ -9720,15 +9764,26 @@ Extensions for DI - + + + Add services such as the IKrakenRestClient and IKrakenSocketClient. Configures the services based on the provided configuration. + + The service collection + The configuration(section) containing the options + + + - Add the IKrakenClient and IKrakenSocketClient to the sevice collection so they can be injected + Add services such as the IKrakenRestClient and IKrakenSocketClient. Services will be configured based on the provided options. The service collection - Set default options for the rest client - Set default options for the socket client - The lifetime of the IKrakenSocketClient for the service collection. Defaults to Singleton. + Set options for the Kraken services + + + DEPRECATED; use instead + + diff --git a/Kraken.Net/KrakenEnvironment.cs b/Kraken.Net/KrakenEnvironment.cs index 5b6b6ff..1745977 100644 --- a/Kraken.Net/KrakenEnvironment.cs +++ b/Kraken.Net/KrakenEnvironment.cs @@ -46,6 +46,26 @@ internal KrakenEnvironment(string name, FuturesSocketBaseAddress = futuresSocketBaseAddress; } + /// + /// ctor for DI, use for creating a custom environment + /// +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + public KrakenEnvironment() : base(TradeEnvironmentNames.Live) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + { } + + /// + /// Get the Kraken environment by name + /// + public static KrakenEnvironment? GetEnvironmentByName(string? name) + => name switch + { + TradeEnvironmentNames.Live => Live, + "" => Live, + null => Live, + _ => default + }; + /// /// Live environment /// diff --git a/Kraken.Net/Objects/Options/KrakenOptions.cs b/Kraken.Net/Objects/Options/KrakenOptions.cs new file mode 100644 index 0000000..3d43d9c --- /dev/null +++ b/Kraken.Net/Objects/Options/KrakenOptions.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Kraken.Net.Objects.Options +{ + /// + /// Kraken options + /// + public class KrakenOptions + { + /// + /// Rest client options + /// + public KrakenRestOptions Rest { get; set; } = new KrakenRestOptions(); + + /// + /// Socket client options + /// + public KrakenSocketOptions Socket { get; set; } = new KrakenSocketOptions(); + + /// + /// Trade environment. Contains info about URL's to use to connect to the API. Use `KrakenEnvironment` to swap environment, for example `Environment = KrakenEnvironment.Live` + /// + public KrakenEnvironment? Environment { get; set; } + + /// + /// The api credentials used for signing requests. + /// + public ApiCredentials? ApiCredentials { get; set; } + + /// + /// The DI service lifetime for the IKrakenSocketClient + /// + public ServiceLifetime? SocketClientLifeTime { get; set; } + } +} diff --git a/Kraken.Net/Objects/Options/KrakenRestOptions.cs b/Kraken.Net/Objects/Options/KrakenRestOptions.cs index 808059d..401ee1e 100644 --- a/Kraken.Net/Objects/Options/KrakenRestOptions.cs +++ b/Kraken.Net/Objects/Options/KrakenRestOptions.cs @@ -10,11 +10,19 @@ public class KrakenRestOptions : RestExchangeOptions /// /// Default options for new KrakenRestClients /// - public static KrakenRestOptions Default { get; set; } = new KrakenRestOptions() + internal static KrakenRestOptions Default { get; set; } = new KrakenRestOptions() { Environment = KrakenEnvironment.Live }; + /// + /// ctor + /// + public KrakenRestOptions() + { + Default?.Set(this); + } + /// /// The static password configured as two-factor authentication for the API key. Will be send as otp parameter on private requests. /// @@ -35,14 +43,14 @@ public class KrakenRestOptions : RestExchangeOptions /// public RestApiOptions FuturesOptions { get; private set; } = new RestApiOptions(); - internal KrakenRestOptions Copy() + internal KrakenRestOptions Set(KrakenRestOptions targetOptions) { - var options = Copy(); - options.SpotOptions = SpotOptions.Copy(); - options.FuturesOptions = FuturesOptions.Copy(); - options.NonceProvider = NonceProvider; - options.StaticTwoFactorAuthenticationPassword = StaticTwoFactorAuthenticationPassword; - return options; + targetOptions = base.Set(targetOptions); + targetOptions.StaticTwoFactorAuthenticationPassword = StaticTwoFactorAuthenticationPassword; + targetOptions.NonceProvider = NonceProvider; + targetOptions.SpotOptions = SpotOptions.Set(targetOptions.SpotOptions); + targetOptions.FuturesOptions = FuturesOptions.Set(targetOptions.FuturesOptions); + return targetOptions; } } } diff --git a/Kraken.Net/Objects/Options/KrakenSocketOptions.cs b/Kraken.Net/Objects/Options/KrakenSocketOptions.cs index 094688d..c644411 100644 --- a/Kraken.Net/Objects/Options/KrakenSocketOptions.cs +++ b/Kraken.Net/Objects/Options/KrakenSocketOptions.cs @@ -10,12 +10,20 @@ public class KrakenSocketOptions : SocketExchangeOptions /// /// Default options for new KrakenRestClients /// - public static KrakenSocketOptions Default { get; set; } = new KrakenSocketOptions() + internal static KrakenSocketOptions Default { get; set; } = new KrakenSocketOptions() { SocketSubscriptionsCombineTarget = 10, Environment = KrakenEnvironment.Live }; + /// + /// ctor + /// + public KrakenSocketOptions() + { + Default?.Set(this); + } + /// /// Optional nonce provider for signing requests. Careful providing a custom provider; once a nonce is sent to the server, every request after that needs a higher nonce than that /// @@ -31,13 +39,13 @@ public class KrakenSocketOptions : SocketExchangeOptions /// public SocketApiOptions FuturesOptions { get; private set; } = new SocketApiOptions() { MaxSocketConnections = 100 }; - internal KrakenSocketOptions Copy() + internal KrakenSocketOptions Set(KrakenSocketOptions targetOptions) { - var options = Copy(); - options.SpotOptions = SpotOptions.Copy(); - options.FuturesOptions = FuturesOptions.Copy(); - options.NonceProvider = NonceProvider; - return options; + targetOptions = base.Set(targetOptions); + targetOptions.NonceProvider = NonceProvider; + targetOptions.SpotOptions = SpotOptions.Set(targetOptions.SpotOptions); + targetOptions.FuturesOptions = FuturesOptions.Set(targetOptions.FuturesOptions); + return targetOptions; } } } From c550b990657af3e48d8af8f16bd42e097824db01 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Tue, 19 Nov 2024 12:59:59 +0100 Subject: [PATCH 2/2] updated CryptoExchange ref --- .../SpotApi/IKrakenSocketClientSpotApi.cs | 24 ++++++------- Kraken.Net/Kraken.Net.csproj | 6 ++-- Kraken.Net/Kraken.Net.xml | 34 ++++++++++++------- Kraken.Net/KrakenExchange.cs | 10 ++++++ docs/index.html | 10 ++++-- 5 files changed, 54 insertions(+), 30 deletions(-) diff --git a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs index 7c4a86a..75f9795 100644 --- a/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs +++ b/Kraken.Net/Interfaces/Clients/SpotApi/IKrakenSocketClientSpotApi.cs @@ -29,7 +29,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to ticker updates /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -39,7 +39,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to ticker updates /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -49,7 +49,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to kline updates /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Kline interval /// Data handler /// Cancellation token for closing this subscription @@ -60,7 +60,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to kline updates /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Kline interval /// Data handler /// Cancellation token for closing this subscription @@ -71,7 +71,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to trade updates /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler /// Whether or not a snapshot of the last trades should be send after subscribing /// Cancellation token for closing this subscription @@ -82,7 +82,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to trade updates /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Data handler /// Whether or not a snapshot of the last trades should be send after subscribing /// Cancellation token for closing this subscription @@ -93,7 +93,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to order book updates. Order book entries are aggregated per price level /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 /// Data handler /// Whether or not a snapshot of the last book state should be send after subscribing @@ -105,7 +105,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to order book updates. Order book entries are aggregated per price level /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 /// Data handler /// Whether or not a snapshot of the last book state should be send after subscribing @@ -118,7 +118,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to the full order book with individual orders. Requires authentication. /// /// - /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Depth of the initial order book snapshot. 10, 100 or 1000 /// Data handler /// Whether or not a snapshot of the last book state should be send after subscribing @@ -130,7 +130,7 @@ public interface IKrakenSocketClientSpotApi : ISocketApiClient, IDisposable /// Subscribe to the full order book with individual orders. Requires authentication. /// /// - /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + /// Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property /// Depth of the initial order book snapshot. 10, 100 or 1000 /// Data handler /// Whether or not a snapshot of the last book state should be send after subscribing @@ -178,7 +178,7 @@ Task> SubscribeToOrderUpdatesAsync( /// Place a new order /// /// - /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the /// The side of the order /// The type of the order /// The quantity of the order @@ -277,7 +277,7 @@ Task> EditOrderAsync( /// Replace an existing order /// /// - /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + /// The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the /// Order id, either this or clientOrderId should be provided /// Client order id, either this or orderId should be provided /// The quantity of the order diff --git a/Kraken.Net/Kraken.Net.csproj b/Kraken.Net/Kraken.Net.csproj index 7bf8807..5bfab4d 100644 --- a/Kraken.Net/Kraken.Net.csproj +++ b/Kraken.Net/Kraken.Net.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1 enable @@ -51,12 +51,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - \ No newline at end of file diff --git a/Kraken.Net/Kraken.Net.xml b/Kraken.Net/Kraken.Net.xml index 9e6138e..2c2eed5 100644 --- a/Kraken.Net/Kraken.Net.xml +++ b/Kraken.Net/Kraken.Net.xml @@ -3269,7 +3269,7 @@ Subscribe to ticker updates - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -3279,7 +3279,7 @@ Subscribe to ticker updates - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler Cancellation token for closing this subscription A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected @@ -3289,7 +3289,7 @@ Subscribe to kline updates - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Kline interval Data handler Cancellation token for closing this subscription @@ -3300,7 +3300,7 @@ Subscribe to kline updates - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Kline interval Data handler Cancellation token for closing this subscription @@ -3311,7 +3311,7 @@ Subscribe to trade updates - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler Whether or not a snapshot of the last trades should be send after subscribing Cancellation token for closing this subscription @@ -3322,7 +3322,7 @@ Subscribe to trade updates - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Data handler Whether or not a snapshot of the last trades should be send after subscribing Cancellation token for closing this subscription @@ -3333,7 +3333,7 @@ Subscribe to order book updates. Order book entries are aggregated per price level - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 Data handler Whether or not a snapshot of the last book state should be send after subscribing @@ -3345,7 +3345,7 @@ Subscribe to order book updates. Order book entries are aggregated per price level - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Depth of the initial order book snapshot. 10, 25, 100, 500 or 1000 Data handler Whether or not a snapshot of the last book state should be send after subscribing @@ -3357,7 +3357,7 @@ Subscribe to the full order book with individual orders. Requires authentication. - Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbols to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Depth of the initial order book snapshot. 10, 100 or 1000 Data handler Whether or not a snapshot of the last book state should be send after subscribing @@ -3369,7 +3369,7 @@ Subscribe to the full order book with individual orders. Requires authentication. - Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property + Symbol to subscribe to, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the WebsocketName property Depth of the initial order book snapshot. 10, 100 or 1000 Data handler Whether or not a snapshot of the last book state should be send after subscribing @@ -3413,7 +3413,7 @@ Place a new order - The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the The side of the order The type of the order The quantity of the order @@ -3468,7 +3468,7 @@ Replace an existing order - The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the + The symbol the order is on, for example `ETH/USDT`. Websocket name of a symbol can be obtained via restClient.SpotApi.ExchangeData.GetSymbolsAsync using the Order id, either this or clientOrderId should be provided Client order id, either this or orderId should be provided The quantity of the order @@ -3729,6 +3729,16 @@ Exchange name + + + Exchange name + + + + + Url to exchange image + + Url to the main website diff --git a/Kraken.Net/KrakenExchange.cs b/Kraken.Net/KrakenExchange.cs index 3134d77..f8edd04 100644 --- a/Kraken.Net/KrakenExchange.cs +++ b/Kraken.Net/KrakenExchange.cs @@ -17,6 +17,16 @@ public static class KrakenExchange /// public static string ExchangeName => "Kraken"; + /// + /// Exchange name + /// + public static string DisplayName => "Kraken"; + + /// + /// Url to exchange image + /// + public static string ImageUrl { get; } = "https://raw.githubusercontent.com/JKorf/Kraken.Net/master/Kraken.Net/Icon/icon.png"; + /// /// Url to the main website /// diff --git a/docs/index.html b/docs/index.html index ec8fde5..82f6b1d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -191,8 +191,14 @@

API Access

Kraken.Net can be configured using Dotnet dependency injection, after which the clients can be injected into your services. It also correctly configures logging and HttpClient usage.

-
builder.Services.AddKraken(options => {
-  // Options can be configured here, for example:
+		  
// Configure options from config file
+// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example
+builder.Services.AddKraken(builder.Configuration.GetSection("Kraken"));
+		  
+// OR
+		  
+ builder.Services.AddKraken(options => {
+  // Configure options in code
   options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
 });