diff --git a/CryptoExchange.Net.UnitTests/OptionsTests.cs b/CryptoExchange.Net.UnitTests/OptionsTests.cs index 8e60710..7af2ac2 100644 --- a/CryptoExchange.Net.UnitTests/OptionsTests.cs +++ b/CryptoExchange.Net.UnitTests/OptionsTests.cs @@ -100,6 +100,10 @@ namespace CryptoExchange.Net.UnitTests Assert.That(authProvider1.GetSecret() == "222"); Assert.That(authProvider2.GetKey() == "123"); Assert.That(authProvider2.GetSecret() == "456"); + + // Cleanup static values + TestClientOptions.Default.ApiCredentials = null; + TestClientOptions.Default.Api1Options.ApiCredentials = null; } [Test] @@ -121,6 +125,10 @@ namespace CryptoExchange.Net.UnitTests Assert.That(authProvider2.GetKey() == "123"); Assert.That(authProvider2.GetSecret() == "456"); Assert.That(client.Api2.BaseAddress == "https://localhost:123"); + + // Cleanup static values + TestClientOptions.Default.ApiCredentials = null; + TestClientOptions.Default.Api1Options.ApiCredentials = null; } } @@ -134,6 +142,14 @@ namespace CryptoExchange.Net.UnitTests Environment = new TestEnvironment("test", "https://test.com") }; + /// + /// ctor + /// + public TestClientOptions() + { + Default?.Set(this); + } + /// /// The default receive window for requests /// @@ -143,12 +159,12 @@ namespace CryptoExchange.Net.UnitTests public RestApiOptions Api2Options { get; set; } = new RestApiOptions(); - internal TestClientOptions Copy() + internal TestClientOptions Set(TestClientOptions targetOptions) { - var options = Copy(); - options.Api1Options = Api1Options.Copy(); - options.Api2Options = Api2Options.Copy(); - return options; + targetOptions = base.Set(targetOptions); + targetOptions.Api1Options = Api1Options.Set(targetOptions.Api1Options); + targetOptions.Api2Options = Api2Options.Set(targetOptions.Api2Options); + return targetOptions; } } } diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs index e32a468..57abfe8 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestBaseClient.cs @@ -20,7 +20,7 @@ namespace CryptoExchange.Net.UnitTests public TestBaseClient(): base(null, "Test") { - var options = TestClientOptions.Default.Copy(); + var options = new TestClientOptions(); Initialize(options); SubClient = AddApiClient(new TestSubClient(options, new RestApiOptions())); } diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs index d739e57..9499613 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestRestClient.cs @@ -16,6 +16,7 @@ using CryptoExchange.Net.Objects.Options; using Microsoft.Extensions.Logging; using CryptoExchange.Net.Clients; using CryptoExchange.Net.SharedApis; +using Microsoft.Extensions.Options; namespace CryptoExchange.Net.UnitTests.TestImplementations { @@ -24,22 +25,17 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations public TestRestApi1Client Api1 { get; } public TestRestApi2Client Api2 { get; } - public TestRestClient(Action optionsFunc) : this(optionsFunc, null) + public TestRestClient(Action? optionsDelegate = null) + : this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate))) { } - public TestRestClient(ILoggerFactory loggerFactory = null, HttpClient httpClient = null) : this((x) => { }, httpClient, loggerFactory) + public TestRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, IOptions options) : base(loggerFactory, "Test") { - } + Initialize(options.Value); - public TestRestClient(Action optionsFunc, HttpClient httpClient = null, ILoggerFactory loggerFactory = null) : base(loggerFactory, "Test") - { - var options = TestClientOptions.Default.Copy(); - optionsFunc(options); - Initialize(options); - - Api1 = new TestRestApi1Client(options); - Api2 = new TestRestApi2Client(options); + Api1 = new TestRestApi1Client(options.Value); + Api2 = new TestRestApi2Client(options.Value); } public void SetResponse(string responseData, out IRequest requestObj) diff --git a/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs b/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs index e264611..f31f22f 100644 --- a/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs +++ b/CryptoExchange.Net.UnitTests/TestImplementations/TestSocketClient.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.Logging; using Moq; using CryptoExchange.Net.Testing.Implementations; using CryptoExchange.Net.SharedApis; +using Microsoft.Extensions.Options; namespace CryptoExchange.Net.UnitTests.TestImplementations { @@ -22,25 +23,20 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations { public TestSubSocketClient SubClient { get; } - public TestSocketClient(ILoggerFactory loggerFactory = null) : this((x) => { }, loggerFactory) - { - } - /// /// Create a new instance of KucoinSocketClient /// /// Configure the options to use for this client - public TestSocketClient(Action optionsFunc) : this(optionsFunc, null) + public TestSocketClient(Action? optionsDelegate = null) + : this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null) { } - public TestSocketClient(Action optionsFunc, ILoggerFactory loggerFactory = null) : base(loggerFactory, "Test") + public TestSocketClient(IOptions options, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Test") { - var options = TestSocketOptions.Default.Copy(); - optionsFunc(options); - Initialize(options); + Initialize(options.Value); - SubClient = AddApiClient(new TestSubSocketClient(options, options.SubOptions)); + SubClient = AddApiClient(new TestSubSocketClient(options.Value, options.Value.SubOptions)); SubClient.SocketFactory = new Mock().Object; Mock.Get(SubClient.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny(), It.IsAny())).Returns(new TestSocket("https://test.com")); } @@ -70,7 +66,22 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations Environment = new TestEnvironment("Live", "https://test.test") }; + /// + /// ctor + /// + public TestSocketOptions() + { + Default?.Set(this); + } + public SocketApiOptions SubOptions { get; set; } = new SocketApiOptions(); + + internal TestSocketOptions Set(TestSocketOptions targetOptions) + { + targetOptions = base.Set(targetOptions); + targetOptions.SubOptions = SubOptions.Set(targetOptions.SubOptions); + return targetOptions; + } } public class TestSubSocketClient : SocketApiClient diff --git a/CryptoExchange.Net/Authentication/ApiCredentials.cs b/CryptoExchange.Net/Authentication/ApiCredentials.cs index 4c2b899..8f16d26 100644 --- a/CryptoExchange.Net/Authentication/ApiCredentials.cs +++ b/CryptoExchange.Net/Authentication/ApiCredentials.cs @@ -13,39 +13,30 @@ namespace CryptoExchange.Net.Authentication /// /// The api key / label to authenticate requests /// - public string Key { get; } + public string Key { get; set; } /// /// The api secret or private key to authenticate requests /// - public string Secret { get; } + public string Secret { get; set; } /// /// Type of the credentials /// - public ApiCredentialsType CredentialType { get; } + public ApiCredentialsType CredentialType { get; set; } /// /// Create Api credentials providing an api key and secret for authentication /// /// The api key / label used for identification /// The api secret or private key used for signing - public ApiCredentials(string key, string secret) : this(key, secret, ApiCredentialsType.Hmac) - { - } - - /// - /// Create Api credentials providing an api key and secret for authentication - /// - /// The api key / label used for identification - /// The api secret or private key used for signing - /// The type of credentials - public ApiCredentials(string key, string secret, ApiCredentialsType credentialsType) + /// The type of credentials + public ApiCredentials(string key, string secret, ApiCredentialsType credentialType = ApiCredentialsType.Hmac) { if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(secret)) throw new ArgumentException("Key and secret can't be null/empty"); - CredentialType = credentialsType; + CredentialType = credentialType; Key = key; Secret = secret; } @@ -65,7 +56,7 @@ namespace CryptoExchange.Net.Authentication /// The stream containing the json data /// A key to identify the credentials for the API. For example, when set to `binanceKey` the json data should contain a value for the property `binanceKey`. Defaults to 'apiKey'. /// A key to identify the credentials for the API. For example, when set to `binanceSecret` the json data should contain a value for the property `binanceSecret`. Defaults to 'apiSecret'. - public ApiCredentials(Stream inputStream, string? identifierKey = null, string? identifierSecret = null) + public static ApiCredentials FromStream(Stream inputStream, string? identifierKey = null, string? identifierSecret = null) { var accessor = new SystemTextJsonStreamMessageAccessor(); if (!accessor.Read(inputStream, false).Result) @@ -75,11 +66,9 @@ namespace CryptoExchange.Net.Authentication var secret = accessor.GetValue(MessagePath.Get().Property(identifierSecret ?? "apiSecret")); if (key == null || secret == null) throw new ArgumentException("apiKey or apiSecret value not found in Json credential file"); - - Key = key; - Secret = secret; - + inputStream.Seek(0, SeekOrigin.Begin); + return new ApiCredentials(key, secret); } } } diff --git a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs index a3ad2c0..0a201f3 100644 --- a/CryptoExchange.Net/Authentication/AuthenticationProvider.cs +++ b/CryptoExchange.Net/Authentication/AuthenticationProvider.cs @@ -31,7 +31,7 @@ namespace CryptoExchange.Net.Authentication /// /// Get the API key of the current credentials /// - public string ApiKey => _credentials.Key; + public string ApiKey => _credentials.Key!; /// /// ctor @@ -39,7 +39,7 @@ namespace CryptoExchange.Net.Authentication /// protected AuthenticationProvider(ApiCredentials credentials) { - if (credentials.Secret == null) + if (credentials.Key == null || credentials.Secret == null) throw new ArgumentException("ApiKey/Secret needed"); _credentials = credentials; diff --git a/CryptoExchange.Net/Clients/BaseClient.cs b/CryptoExchange.Net/Clients/BaseClient.cs index dce39b2..d0740c6 100644 --- a/CryptoExchange.Net/Clients/BaseClient.cs +++ b/CryptoExchange.Net/Clients/BaseClient.cs @@ -109,6 +109,16 @@ namespace CryptoExchange.Net.Clients return apiClient; } + /// + /// Apply the options delegate to a new options instance + /// + protected static T ApplyOptionsDelegate(Action? del) where T: new() + { + var opts = new T(); + del?.Invoke(opts); + return opts; + } + /// /// Dispose /// diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index 8a7c685..9fdc563 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -59,5 +59,6 @@ + \ No newline at end of file diff --git a/CryptoExchange.Net/Objects/ApiProxy.cs b/CryptoExchange.Net/Objects/ApiProxy.cs index 3ba14c5..bc0da4e 100644 --- a/CryptoExchange.Net/Objects/ApiProxy.cs +++ b/CryptoExchange.Net/Objects/ApiProxy.cs @@ -8,30 +8,21 @@ /// /// The host address of the proxy /// - public string Host { get; } + public string Host { get; set; } /// /// The port of the proxy /// - public int Port { get; } + public int Port { get; set; } /// /// The login of the proxy /// - public string? Login { get; } + public string? Login { get; set; } /// /// The password of the proxy /// - public string? Password { get; } - - /// - /// Create new settings for a proxy - /// - /// The proxy hostname/ip - /// The proxy port - public ApiProxy(string host, int port): this(host, port, null, null) - { - } + public string? Password { get; set; } /// /// Create new settings for a proxy @@ -40,7 +31,7 @@ /// The proxy port /// The proxy login /// The proxy password - public ApiProxy(string host, int port, string? login, string? password) + public ApiProxy(string host, int port, string? login = null, string? password = null) { Host = host; Port = port; diff --git a/CryptoExchange.Net/Objects/Options/RestApiOptions.cs b/CryptoExchange.Net/Objects/Options/RestApiOptions.cs index 259a2ab..244fb43 100644 --- a/CryptoExchange.Net/Objects/Options/RestApiOptions.cs +++ b/CryptoExchange.Net/Objects/Options/RestApiOptions.cs @@ -19,19 +19,15 @@ namespace CryptoExchange.Net.Objects.Options public TimeSpan? TimestampRecalculationInterval { get; set; } /// - /// Create a copy of this options + /// Set the values of this options on the target options /// - /// - /// - public virtual T Copy() where T : RestApiOptions, new() + public T Set(T item) where T : RestApiOptions, new() { - return new T - { - ApiCredentials = ApiCredentials?.Copy(), - OutputOriginalData = OutputOriginalData, - AutoTimestamp = AutoTimestamp, - TimestampRecalculationInterval = TimestampRecalculationInterval - }; + item.ApiCredentials = ApiCredentials?.Copy(); + item.OutputOriginalData = OutputOriginalData; + item.AutoTimestamp = AutoTimestamp; + item.TimestampRecalculationInterval = TimestampRecalculationInterval; + return item; } } diff --git a/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs b/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs index 80a5e96..25e4608 100644 --- a/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs +++ b/CryptoExchange.Net/Objects/Options/RestExchangeOptions.cs @@ -29,25 +29,21 @@ namespace CryptoExchange.Net.Objects.Options public TimeSpan CachingMaxAge { get; set; } = TimeSpan.FromSeconds(5); /// - /// Create a copy of this options + /// Set the values of this options on the target options /// - /// - /// - public T Copy() where T : RestExchangeOptions, new() + public T Set(T item) where T : RestExchangeOptions, new() { - return new T - { - OutputOriginalData = OutputOriginalData, - AutoTimestamp = AutoTimestamp, - TimestampRecalculationInterval = TimestampRecalculationInterval, - ApiCredentials = ApiCredentials?.Copy(), - Proxy = Proxy, - RequestTimeout = RequestTimeout, - RateLimiterEnabled = RateLimiterEnabled, - RateLimitingBehaviour = RateLimitingBehaviour, - CachingEnabled = CachingEnabled, - CachingMaxAge = CachingMaxAge, - }; + item.OutputOriginalData = OutputOriginalData; + item.AutoTimestamp = AutoTimestamp; + item.TimestampRecalculationInterval = TimestampRecalculationInterval; + item.ApiCredentials = ApiCredentials?.Copy(); + item.Proxy = Proxy; + item.RequestTimeout = RequestTimeout; + item.RateLimiterEnabled = RateLimiterEnabled; + item.RateLimitingBehaviour = RateLimitingBehaviour; + item.CachingEnabled = CachingEnabled; + item.CachingMaxAge = CachingMaxAge; + return item; } } @@ -66,15 +62,13 @@ namespace CryptoExchange.Net.Objects.Options #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. /// - /// Create a copy of this options + /// Set the values of this options on the target options /// - /// - /// - public new T Copy() where T : RestExchangeOptions, new() + public new T Set(T target) where T : RestExchangeOptions, new() { - var result = base.Copy(); - result.Environment = Environment; - return result; + base.Set(target); + target.Environment = Environment; + return target; } } diff --git a/CryptoExchange.Net/Objects/Options/SocketApiOptions.cs b/CryptoExchange.Net/Objects/Options/SocketApiOptions.cs index da43509..b41f144 100644 --- a/CryptoExchange.Net/Objects/Options/SocketApiOptions.cs +++ b/CryptoExchange.Net/Objects/Options/SocketApiOptions.cs @@ -20,19 +20,15 @@ namespace CryptoExchange.Net.Objects.Options public int? MaxSocketConnections { get; set; } /// - /// Create a copy of this options + /// Set the values of this options on the target options /// - /// - /// - public T Copy() where T : SocketApiOptions, new() + public T Set(T item) where T : SocketApiOptions, new() { - return new T - { - ApiCredentials = ApiCredentials?.Copy(), - OutputOriginalData = OutputOriginalData, - SocketNoDataTimeout = SocketNoDataTimeout, - MaxSocketConnections = MaxSocketConnections, - }; + item.ApiCredentials = ApiCredentials?.Copy(); + item.OutputOriginalData = OutputOriginalData; + item.SocketNoDataTimeout = SocketNoDataTimeout; + item.MaxSocketConnections = MaxSocketConnections; + return item; } } diff --git a/CryptoExchange.Net/Objects/Options/SocketExchangeOptions.cs b/CryptoExchange.Net/Objects/Options/SocketExchangeOptions.cs index 63c6a5a..babe3f5 100644 --- a/CryptoExchange.Net/Objects/Options/SocketExchangeOptions.cs +++ b/CryptoExchange.Net/Objects/Options/SocketExchangeOptions.cs @@ -57,24 +57,22 @@ namespace CryptoExchange.Net.Objects.Options /// /// /// - public T Copy() where T : SocketExchangeOptions, new() + public T Set(T item) where T : SocketExchangeOptions, new() { - return new T - { - ApiCredentials = ApiCredentials?.Copy(), - OutputOriginalData = OutputOriginalData, - ReconnectPolicy = ReconnectPolicy, - DelayAfterConnect = DelayAfterConnect, - MaxConcurrentResubscriptionsPerSocket = MaxConcurrentResubscriptionsPerSocket, - ReconnectInterval = ReconnectInterval, - SocketNoDataTimeout = SocketNoDataTimeout, - SocketSubscriptionsCombineTarget = SocketSubscriptionsCombineTarget, - MaxSocketConnections = MaxSocketConnections, - Proxy = Proxy, - RequestTimeout = RequestTimeout, - RateLimitingBehaviour = RateLimitingBehaviour, - RateLimiterEnabled = RateLimiterEnabled, - }; + item.ApiCredentials = ApiCredentials?.Copy(); + item.OutputOriginalData = OutputOriginalData; + item.ReconnectPolicy = ReconnectPolicy; + item.DelayAfterConnect = DelayAfterConnect; + item.MaxConcurrentResubscriptionsPerSocket = MaxConcurrentResubscriptionsPerSocket; + item.ReconnectInterval = ReconnectInterval; + item.SocketNoDataTimeout = SocketNoDataTimeout; + item.SocketSubscriptionsCombineTarget = SocketSubscriptionsCombineTarget; + item.MaxSocketConnections = MaxSocketConnections; + item.Proxy = Proxy; + item.RequestTimeout = RequestTimeout; + item.RateLimitingBehaviour = RateLimitingBehaviour; + item.RateLimiterEnabled = RateLimiterEnabled; + return item; } } @@ -93,15 +91,13 @@ namespace CryptoExchange.Net.Objects.Options #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. /// - /// Create a copy of this options + /// Set the values of this options on the target options /// - /// - /// - public new T Copy() where T : SocketExchangeOptions, new() + public new T Set(T target) where T : SocketExchangeOptions, new() { - var result = base.Copy(); - result.Environment = Environment; - return result; + base.Set(target); + target.Environment = Environment; + return target; } } diff --git a/CryptoExchange.Net/Objects/TradeEnvironment.cs b/CryptoExchange.Net/Objects/TradeEnvironment.cs index 70dcbae..7689b31 100644 --- a/CryptoExchange.Net/Objects/TradeEnvironment.cs +++ b/CryptoExchange.Net/Objects/TradeEnvironment.cs @@ -24,14 +24,14 @@ /// /// Name of the environment /// - public string EnvironmentName { get; init; } + public string Name { get; set; } /// /// /// protected TradeEnvironment(string name) { - EnvironmentName = name; + Name = name; } } } diff --git a/CryptoExchange.Net/Requests/RequestFactory.cs b/CryptoExchange.Net/Requests/RequestFactory.cs index 9f007e5..83ea61d 100644 --- a/CryptoExchange.Net/Requests/RequestFactory.cs +++ b/CryptoExchange.Net/Requests/RequestFactory.cs @@ -11,7 +11,7 @@ namespace CryptoExchange.Net.Requests /// public class RequestFactory : IRequestFactory { - private HttpClient? _httpClient; + private HttpClient? _httpClient; /// public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null) diff --git a/Examples/example-config.json b/Examples/example-config.json new file mode 100644 index 0000000..98f1037 --- /dev/null +++ b/Examples/example-config.json @@ -0,0 +1,26 @@ +{ + "ExchangeApiOptions": { + "ApiCredentials": { + "Key": "APIKEY", + "Secret": "SECRET" + }, + "Environment": { + "name": "live" + }, + "Rest":{ + "RequestTimeout": "00:00:20", + "CachingEnabled": true, + "OutputOriginalData": true, + "Proxy": { + "Host": "https://127.0.0.1", + "Port": 8080, + "Login": "User", + "Password": "Pass" + } + }, + "Socket":{ + "RequestTimeout": "00:00:05", + "SocketSubscriptionsCombineTarget": 15 + } + } +} \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index bcc0f18..a638ec6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -356,7 +356,7 @@

Dependency Injection

- All client libraries support and encourage usage via the Dotnet dependency injection system. Add all necesary services per exchange by calling the Add[Library](); extension method on the service collection, or use AddCryptoClients() to add all exchange services in a single class. Options for the clients can be passed as parameters. + All client libraries support and encourage usage via the Dotnet dependency injection system. Add all necesary services per exchange by calling the Add[Library](); extension method on the service collection, or use AddCryptoClients() to add all exchange services in a single class. Options for the clients can be passed as parameters or read from the configuration. See Options.

Using the dependecy injection mechanism also makes sure the HttpClient is used correctly
@@ -2334,7 +2334,7 @@ options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY

Setting options

Dependency injection -

When adding a library to the service collection (see Dependency Injection) the options for the clients can be provided as argument to the calls. Options are split between the REST and the websocket client.

+

When adding a library to the service collection (see Dependency Injection) the options for the clients can be provided as argument to the calls or read from configuration.

builder.Services.AddBinance(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBinance(builder.Configuration.GetSection("Binance"));
builder.Services.AddBingX(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBingX(builder.Configuration.GetSection("BingX"));
builder.Services.AddBitfinex(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBitfinex(builder.Configuration.GetSection("Bitfinex"));
builder.Services.AddBitget(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBitget(builder.Configuration.GetSection("Bitget"));
builder.Services.AddBitMart(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBitMart(builder.Configuration.GetSection("BitMart"));
builder.Services.AddBybit(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddBybit(builder.Configuration.GetSection("Bybit"));
builder.Services.AddCoinbase(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddCoinbase(builder.Configuration.GetSection("Coinbase"));
builder.Services.AddCoinGecko(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  });
+ options => { + options.RequestTimeout = TimeSpan.FromSeconds(30); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddCoinGecko(builder.Configuration.GetSection("CoinGecko"));
builder.Services.AddCoinEx(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddCoinEx(builder.Configuration.GetSection("CoinEx"));
builder.Services.AddCryptoCom(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddCryptoCom(builder.Configuration.GetSection("CryptoCom"));
builder.Services.AddGateIo(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddGateIo(builder.Configuration.GetSection("GateIo"));
builder.Services.AddHTX(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddHTX(builder.Configuration.GetSection("HTX"));
builder.Services.AddKraken(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddKraken(builder.Configuration.GetSection("Kraken"));
builder.Services.AddKucoin(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddKucoin(builder.Configuration.GetSection("Kucoin"));
builder.Services.AddMexc(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddMexc(builder.Configuration.GetSection("Mexc"));
builder.Services.AddOKX(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddOKX(builder.Configuration.GetSection("OKX"));
builder.Services.AddWhiteBit(
-  restOptions => {
-    restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
-  },
-  socketOptions => {
-    socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
-  });
+ options => { + options.Rest.RequestTimeout = TimeSpan.FromSeconds(30); + options.Socket.RequestTimeout = TimeSpan.FromSeconds(5); + }); + +// OR + +// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example configuration +builder.Services.AddWhiteBit(builder.Configuration.GetSection("WhiteBit"));