1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-06 15:36:30 +00:00

Client Configuration (#219)

Added support for IOptions injection, allowing options to be read from IConfiguration
Small refactor on client options internals
Updated HttpClient to be static field to be
This commit is contained in:
Jan Korf 2024-11-19 11:44:30 +01:00 committed by GitHub
parent 48797038be
commit 7d7bc35869
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 312 additions and 237 deletions

View File

@ -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")
};
/// <summary>
/// ctor
/// </summary>
public TestClientOptions()
{
Default?.Set(this);
}
/// <summary>
/// The default receive window for requests
/// </summary>
@ -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<TestClientOptions>();
options.Api1Options = Api1Options.Copy<RestApiOptions>();
options.Api2Options = Api2Options.Copy<RestApiOptions>();
return options;
targetOptions = base.Set<TestClientOptions>(targetOptions);
targetOptions.Api1Options = Api1Options.Set(targetOptions.Api1Options);
targetOptions.Api2Options = Api2Options.Set(targetOptions.Api2Options);
return targetOptions;
}
}
}

View File

@ -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()));
}

View File

@ -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<TestClientOptions> optionsFunc) : this(optionsFunc, null)
public TestRestClient(Action<TestClientOptions>? 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<TestClientOptions> options) : base(loggerFactory, "Test")
{
}
Initialize(options.Value);
public TestRestClient(Action<TestClientOptions> 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)

View File

@ -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)
{
}
/// <summary>
/// Create a new instance of KucoinSocketClient
/// </summary>
/// <param name="optionsFunc">Configure the options to use for this client</param>
public TestSocketClient(Action<TestSocketOptions> optionsFunc) : this(optionsFunc, null)
public TestSocketClient(Action<TestSocketOptions>? optionsDelegate = null)
: this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null)
{
}
public TestSocketClient(Action<TestSocketOptions> optionsFunc, ILoggerFactory loggerFactory = null) : base(loggerFactory, "Test")
public TestSocketClient(IOptions<TestSocketOptions> options, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Test")
{
var options = TestSocketOptions.Default.Copy<TestSocketOptions>();
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<IWebsocketFactory>().Object;
Mock.Get(SubClient.SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny<ILogger>(), It.IsAny<WebSocketParameters>())).Returns(new TestSocket("https://test.com"));
}
@ -70,7 +66,22 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
Environment = new TestEnvironment("Live", "https://test.test")
};
/// <summary>
/// ctor
/// </summary>
public TestSocketOptions()
{
Default?.Set(this);
}
public SocketApiOptions SubOptions { get; set; } = new SocketApiOptions();
internal TestSocketOptions Set(TestSocketOptions targetOptions)
{
targetOptions = base.Set<TestSocketOptions>(targetOptions);
targetOptions.SubOptions = SubOptions.Set(targetOptions.SubOptions);
return targetOptions;
}
}
public class TestSubSocketClient : SocketApiClient

View File

@ -13,39 +13,30 @@ namespace CryptoExchange.Net.Authentication
/// <summary>
/// The api key / label to authenticate requests
/// </summary>
public string Key { get; }
public string Key { get; set; }
/// <summary>
/// The api secret or private key to authenticate requests
/// </summary>
public string Secret { get; }
public string Secret { get; set; }
/// <summary>
/// Type of the credentials
/// </summary>
public ApiCredentialsType CredentialType { get; }
public ApiCredentialsType CredentialType { get; set; }
/// <summary>
/// Create Api credentials providing an api key and secret for authentication
/// </summary>
/// <param name="key">The api key / label used for identification</param>
/// <param name="secret">The api secret or private key used for signing</param>
public ApiCredentials(string key, string secret) : this(key, secret, ApiCredentialsType.Hmac)
{
}
/// <summary>
/// Create Api credentials providing an api key and secret for authentication
/// </summary>
/// <param name="key">The api key / label used for identification</param>
/// <param name="secret">The api secret or private key used for signing</param>
/// <param name="credentialsType">The type of credentials</param>
public ApiCredentials(string key, string secret, ApiCredentialsType credentialsType)
/// <param name="credentialType">The type of credentials</param>
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
/// <param name="inputStream">The stream containing the json data</param>
/// <param name="identifierKey">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'.</param>
/// <param name="identifierSecret">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'.</param>
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<string>(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);
}
}
}

View File

@ -31,7 +31,7 @@ namespace CryptoExchange.Net.Authentication
/// <summary>
/// Get the API key of the current credentials
/// </summary>
public string ApiKey => _credentials.Key;
public string ApiKey => _credentials.Key!;
/// <summary>
/// ctor
@ -39,7 +39,7 @@ namespace CryptoExchange.Net.Authentication
/// <param name="credentials"></param>
protected AuthenticationProvider(ApiCredentials credentials)
{
if (credentials.Secret == null)
if (credentials.Key == null || credentials.Secret == null)
throw new ArgumentException("ApiKey/Secret needed");
_credentials = credentials;

View File

@ -109,6 +109,16 @@ namespace CryptoExchange.Net.Clients
return apiClient;
}
/// <summary>
/// Apply the options delegate to a new options instance
/// </summary>
protected static T ApplyOptionsDelegate<T>(Action<T>? del) where T: new()
{
var opts = new T();
del?.Invoke(opts);
return opts;
}
/// <summary>
/// Dispose
/// </summary>

View File

@ -59,5 +59,6 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@ -8,30 +8,21 @@
/// <summary>
/// The host address of the proxy
/// </summary>
public string Host { get; }
public string Host { get; set; }
/// <summary>
/// The port of the proxy
/// </summary>
public int Port { get; }
public int Port { get; set; }
/// <summary>
/// The login of the proxy
/// </summary>
public string? Login { get; }
public string? Login { get; set; }
/// <summary>
/// The password of the proxy
/// </summary>
public string? Password { get; }
/// <summary>
/// Create new settings for a proxy
/// </summary>
/// <param name="host">The proxy hostname/ip</param>
/// <param name="port">The proxy port</param>
public ApiProxy(string host, int port): this(host, port, null, null)
{
}
public string? Password { get; set; }
/// <summary>
/// Create new settings for a proxy
@ -40,7 +31,7 @@
/// <param name="port">The proxy port</param>
/// <param name="login">The proxy login</param>
/// <param name="password">The proxy password</param>
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;

View File

@ -19,19 +19,15 @@ namespace CryptoExchange.Net.Objects.Options
public TimeSpan? TimestampRecalculationInterval { get; set; }
/// <summary>
/// Create a copy of this options
/// Set the values of this options on the target options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T Copy<T>() where T : RestApiOptions, new()
public T Set<T>(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;
}
}

View File

@ -29,25 +29,21 @@ namespace CryptoExchange.Net.Objects.Options
public TimeSpan CachingMaxAge { get; set; } = TimeSpan.FromSeconds(5);
/// <summary>
/// Create a copy of this options
/// Set the values of this options on the target options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : RestExchangeOptions, new()
public T Set<T>(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.
/// <summary>
/// Create a copy of this options
/// Set the values of this options on the target options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public new T Copy<T>() where T : RestExchangeOptions<TEnvironment>, new()
public new T Set<T>(T target) where T : RestExchangeOptions<TEnvironment>, new()
{
var result = base.Copy<T>();
result.Environment = Environment;
return result;
base.Set(target);
target.Environment = Environment;
return target;
}
}

View File

@ -20,19 +20,15 @@ namespace CryptoExchange.Net.Objects.Options
public int? MaxSocketConnections { get; set; }
/// <summary>
/// Create a copy of this options
/// Set the values of this options on the target options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : SocketApiOptions, new()
public T Set<T>(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;
}
}

View File

@ -57,24 +57,22 @@ namespace CryptoExchange.Net.Objects.Options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Copy<T>() where T : SocketExchangeOptions, new()
public T Set<T>(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.
/// <summary>
/// Create a copy of this options
/// Set the values of this options on the target options
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public new T Copy<T>() where T : SocketExchangeOptions<TEnvironment>, new()
public new T Set<T>(T target) where T : SocketExchangeOptions<TEnvironment>, new()
{
var result = base.Copy<T>();
result.Environment = Environment;
return result;
base.Set(target);
target.Environment = Environment;
return target;
}
}

View File

@ -24,14 +24,14 @@
/// <summary>
/// Name of the environment
/// </summary>
public string EnvironmentName { get; init; }
public string Name { get; set; }
/// <summary>
/// </summary>
/// <param name="name"></param>
protected TradeEnvironment(string name)
{
EnvironmentName = name;
Name = name;
}
}
}

View File

@ -11,7 +11,7 @@ namespace CryptoExchange.Net.Requests
/// </summary>
public class RequestFactory : IRequestFactory
{
private HttpClient? _httpClient;
private HttpClient? _httpClient;
/// <inheritdoc />
public void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? client = null)

View File

@ -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
}
}
}

View File

@ -356,7 +356,7 @@
<section id="idocs_di">
<h2>Dependency Injection</h2>
<p>
All client libraries support and encourage usage via the Dotnet dependency injection system. Add all necesary services per exchange by calling the <code>Add[Library]();</code> extension method on the service collection, or use <code>AddCryptoClients()</code> to add all exchange services in a single class. <a href="#idocs_options_set">Options</a> 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 <code>Add[Library]();</code> extension method on the service collection, or use <code>AddCryptoClients()</code> to add all exchange services in a single class. Options for the clients can be passed as parameters or read from the configuration. See <a href="#idocs_options_set">Options</a>.
</p>
<div class="alert alert-info">Using the dependecy injection mechanism also makes sure the HttpClient is used correctly</div>
<div class="tab-wrap">
@ -2334,7 +2334,7 @@ options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY
<h2>Setting options</h2>
<b>Dependency injection</b>
<p>When adding a library to the service collection (see <a href="#idocs_di">Dependency Injection</a>) the options for the clients can be provided as argument to the calls. Options are split between the REST and the websocket client.</p>
<p>When adding a library to the service collection (see <a href="#idocs_di">Dependency Injection</a>) the options for the clients can be provided as argument to the calls or read from configuration.</p>
<div class="tab-wrap">
<ul class="nav nav-tabs" id="options" role="tablist" style="margin-bottom: -16px;">
<li class="nav-item" role="presentation">
@ -2404,153 +2404,206 @@ options.ApiCredentials = new ApiCredentials("YOUR PUBLIC KEY", "YOUR PRIVATE KEY
</div>
<div class="tab-pane fade" id="options-binance" role="tabpanel" aria-labelledby="options-binance-tab">
<pre><code>builder.Services.AddBinance(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-bingx" role="tabpanel" aria-labelledby="options-bingx-tab">
<pre><code>builder.Services.AddBingX(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre></code></pre>
</div>
<div class="tab-pane fade" id="options-bitfinex" role="tabpanel" aria-labelledby="options-bitfinex-tab">
<pre><code>builder.Services.AddBitfinex(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-bitget" role="tabpanel" aria-labelledby="options-bitget-tab">
<pre><code>builder.Services.AddBitget(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-bitmart" role="tabpanel" aria-labelledby="options-bitmart-tab">
<pre><code>builder.Services.AddBitMart(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-bybit" role="tabpanel" aria-labelledby="options-bybit-tab">
<pre><code>builder.Services.AddBybit(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-coinbase" role="tabpanel" aria-labelledby="options-coinbase-tab">
<pre><code>builder.Services.AddCoinbase(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-coingecko" role="tabpanel" aria-labelledby="options-coingecko-tab">
<pre><code>builder.Services.AddCoinGecko(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-coinex" role="tabpanel" aria-labelledby="options-coinex-tab">
<pre><code>builder.Services.AddCoinEx(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-cryptocom" role="tabpanel" aria-labelledby="options-cryptocom-tab">
<pre><code>builder.Services.AddCryptoCom(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-gateio" role="tabpanel" aria-labelledby="options-gateio-tab">
<pre><code>builder.Services.AddGateIo(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-htx" role="tabpanel" aria-labelledby="options-htx-tab">
<pre><code>builder.Services.AddHTX(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-kraken" role="tabpanel" aria-labelledby="options-kraken-tab">
<pre><code>builder.Services.AddKraken(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-kucoin" role="tabpanel" aria-labelledby="options-kucoin-tab">
<pre><code>builder.Services.AddKucoin(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-mexc" role="tabpanel" aria-labelledby="options-mexc-tab">
<pre><code>builder.Services.AddMexc(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-okx" role="tabpanel" aria-labelledby="options-okx-tab">
<pre><code>builder.Services.AddOKX(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
<div class="tab-pane fade" id="options-whitebit" role="tabpanel" aria-labelledby="options-whitebit-tab">
<pre><code>builder.Services.AddWhiteBit(
restOptions => {
restOptions.RequestTimeout = TimeSpan.FromSeconds(30);
},
socketOptions => {
socketOptions.RequestTimeout = TimeSpan.FromSeconds(10);
});</code></pre>
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"));</code></pre>
</div>
</div>
</div>