diff --git a/ByBit.Net/Bybit.Net.csproj b/ByBit.Net/Bybit.Net.csproj index 5172a474..c213ee3f 100644 --- a/ByBit.Net/Bybit.Net.csproj +++ b/ByBit.Net/Bybit.Net.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1 enable @@ -48,7 +48,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/ByBit.Net/BybitEnvironment.cs b/ByBit.Net/BybitEnvironment.cs index 36294b8c..ff29144a 100644 --- a/ByBit.Net/BybitEnvironment.cs +++ b/ByBit.Net/BybitEnvironment.cs @@ -26,6 +26,31 @@ internal BybitEnvironment(string name, SocketBaseAddress = socketBaseAddress; } + /// + /// 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 BybitEnvironment() : 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 Bybit environment by name + /// + public static BybitEnvironment? GetEnvironmentByName(string? name) + => name switch + { + TradeEnvironmentNames.Live => Live, + TradeEnvironmentNames.Testnet => Testnet, + "Netherlands" => Netherlands, + "Hongkong" => HongKong, + "Turkey" => Turkey, + "Demo" => DemoTrading, + "" => Live, + null => Live, + _ => default + }; + /// /// Live environment /// @@ -46,7 +71,7 @@ internal BybitEnvironment(string name, /// Live environment for users from The Netherlands /// public static BybitEnvironment Netherlands { get; } - = new BybitEnvironment(TradeEnvironmentNames.Live, + = new BybitEnvironment("Netherlands", BybitApiAddresses.Netherlands.RestBaseAddress, BybitApiAddresses.Netherlands.SocketBaseAddress); @@ -54,7 +79,7 @@ internal BybitEnvironment(string name, /// Live environment for users from HongKong /// public static BybitEnvironment HongKong { get; } - = new BybitEnvironment(TradeEnvironmentNames.Live, + = new BybitEnvironment("HongKong", BybitApiAddresses.HongKong.RestBaseAddress, BybitApiAddresses.HongKong.SocketBaseAddress); @@ -62,7 +87,7 @@ internal BybitEnvironment(string name, /// Live environment for users from Turkey /// public static BybitEnvironment Turkey { get; } - = new BybitEnvironment(TradeEnvironmentNames.Live, + = new BybitEnvironment("Turkey", BybitApiAddresses.Turkey.RestBaseAddress, BybitApiAddresses.Turkey.SocketBaseAddress); @@ -70,7 +95,7 @@ internal BybitEnvironment(string name, /// Demo trading environment, needs seperate API key. See https://bybit-exchange.github.io/docs/v5/demo /// public static BybitEnvironment DemoTrading { get; } - = new BybitEnvironment(TradeEnvironmentNames.Live, + = new BybitEnvironment("Demo", BybitApiAddresses.DemoTrading.RestBaseAddress, BybitApiAddresses.DemoTrading.SocketBaseAddress); diff --git a/ByBit.Net/BybitExchange.cs b/ByBit.Net/BybitExchange.cs index f9498d48..47a7f5b7 100644 --- a/ByBit.Net/BybitExchange.cs +++ b/ByBit.Net/BybitExchange.cs @@ -14,6 +14,16 @@ public static class BybitExchange /// public static string ExchangeName => "Bybit"; + /// + /// Exchange name + /// + public static string DisplayName => "Bybit"; + + /// + /// Url to exchange image + /// + public static string ImageUrl { get; } = "https://raw.githubusercontent.com/JKorf/Bybit.Net/master/ByBit.Net/Icon/icon.png"; + /// /// Url to the main website /// diff --git a/ByBit.Net/Clients/BybitRestClient.cs b/ByBit.Net/Clients/BybitRestClient.cs index b1b54e40..ef95bd7f 100644 --- a/ByBit.Net/Clients/BybitRestClient.cs +++ b/ByBit.Net/Clients/BybitRestClient.cs @@ -11,6 +11,7 @@ using System; using Microsoft.Extensions.Logging; using CryptoExchange.Net.Clients; +using Microsoft.Extensions.Options; namespace Bybit.Net.Clients { @@ -31,28 +32,26 @@ public class BybitRestClient : BaseRestClient, IBybitRestClient /// Create a new instance of the BybitRestClient using provided options /// /// Option configuration delegate - public BybitRestClient(Action? optionsDelegate = null) : this(null, null, optionsDelegate) + public BybitRestClient(Action? optionsDelegate = null) + : this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate))) { } /// /// Create a new instance of the BybitRestClient /// - /// Option configuration delegate + /// Option configuration delegate /// The logger factory /// Http client for this client - public BybitRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Action? optionsDelegate = null) + public BybitRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, IOptions options) : base(loggerFactory, "Bybit") { - var options = BybitRestOptions.Default.Copy(); - if (optionsDelegate != null) - optionsDelegate(options); - Initialize(options); + Initialize(options.Value); - SpotApiV3 = AddApiClient(new BybitRestClientSpotApiV3(_logger, httpClient, options)); - CopyTradingApi = AddApiClient(new BybitRestClientCopyTradingApi(_logger, httpClient, options)); - DerivativesApi = AddApiClient(new BybitRestClientDerivativesApi(_logger, httpClient, options)); - V5Api = AddApiClient(new V5.BybitRestClientApi(_logger, httpClient, options)); + SpotApiV3 = AddApiClient(new BybitRestClientSpotApiV3(_logger, httpClient, options.Value)); + CopyTradingApi = AddApiClient(new BybitRestClientCopyTradingApi(_logger, httpClient, options.Value)); + DerivativesApi = AddApiClient(new BybitRestClientDerivativesApi(_logger, httpClient, options.Value)); + V5Api = AddApiClient(new V5.BybitRestClientApi(_logger, httpClient, options.Value)); } #endregion @@ -63,9 +62,7 @@ public BybitRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Ac /// Option configuration delegate public static void SetDefaultOptions(Action optionsDelegate) { - var options = BybitRestOptions.Default.Copy(); - optionsDelegate(options); - BybitRestOptions.Default = options; + BybitRestOptions.Default = ApplyOptionsDelegate(optionsDelegate); } /// diff --git a/ByBit.Net/Clients/BybitSocketClient.cs b/ByBit.Net/Clients/BybitSocketClient.cs index 7a910294..35fc312e 100644 --- a/ByBit.Net/Clients/BybitSocketClient.cs +++ b/ByBit.Net/Clients/BybitSocketClient.cs @@ -14,6 +14,7 @@ using Bybit.Net.Clients.DerivativesApi.UnifiedMarginApi; using Bybit.Net.Clients.SpotApi.v3; using CryptoExchange.Net.Clients; +using Microsoft.Extensions.Options; namespace Bybit.Net.Clients { @@ -39,19 +40,12 @@ public class BybitSocketClient : BaseSocketClient, IBybitSocketClient /// public IBybitSocketClientPrivateApi V5PrivateApi { get; } - /// - /// Create a new instance of the BybitSocketClient - /// - /// The logger factory - public BybitSocketClient(ILoggerFactory? loggerFactory = null) : this((x) => { }, loggerFactory) - { - } - /// /// Create a new instance of the BybitSocketClient /// /// Option configuration delegate - public BybitSocketClient(Action optionsDelegate) : this(optionsDelegate, null) + public BybitSocketClient(Action? optionsDelegate = null) + : this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null) { } @@ -59,24 +53,22 @@ public BybitSocketClient(Action optionsDelegate) : this(opti /// Create a new instance of the BybitSocketClient /// /// The logger factory - /// Option configuration delegate - public BybitSocketClient(Action optionsDelegate, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Bybit") + /// Option configuration + public BybitSocketClient(IOptions options, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Bybit") { - var options = BybitSocketOptions.Default.Copy(); - optionsDelegate(options); - Initialize(options); + Initialize(options.Value); - SpotV3Api = AddApiClient(new BybitSocketClientSpotApiV3(_logger, options)); + SpotV3Api = AddApiClient(new BybitSocketClientSpotApiV3(_logger, options.Value)); - DerivativesApi = AddApiClient(new BybitSocketClientDerivativesPublicApi(_logger, options)); - UnifiedMarginApi = AddApiClient(new BybitSocketClientUnifiedMarginApi(_logger, options)); - ContractApi = AddApiClient(new BybitSocketClientContractApi(_logger, options)); + DerivativesApi = AddApiClient(new BybitSocketClientDerivativesPublicApi(_logger, options.Value)); + UnifiedMarginApi = AddApiClient(new BybitSocketClientUnifiedMarginApi(_logger, options.Value)); + ContractApi = AddApiClient(new BybitSocketClientContractApi(_logger, options.Value)); - V5SpotApi = AddApiClient(new BybitSocketClientSpotApi(_logger, options)); - V5InverseApi = AddApiClient(new BybitSocketClientInverseApi(_logger, options)); - V5LinearApi = AddApiClient(new BybitSocketClientLinearApi(_logger, options)); - V5OptionsApi = AddApiClient(new BybitSocketClientOptionApi(_logger, options)); - V5PrivateApi = AddApiClient(new BybitSocketClientPrivateApi(_logger, options)); + V5SpotApi = AddApiClient(new BybitSocketClientSpotApi(_logger, options.Value)); + V5InverseApi = AddApiClient(new BybitSocketClientInverseApi(_logger, options.Value)); + V5LinearApi = AddApiClient(new BybitSocketClientLinearApi(_logger, options.Value)); + V5OptionsApi = AddApiClient(new BybitSocketClientOptionApi(_logger, options.Value)); + V5PrivateApi = AddApiClient(new BybitSocketClientPrivateApi(_logger, options.Value)); } /// @@ -85,9 +77,7 @@ public BybitSocketClient(Action optionsDelegate, ILoggerFact /// Option configuration delegate public static void SetDefaultOptions(Action optionsDelegate) { - var options = BybitSocketOptions.Default.Copy(); - optionsDelegate(options); - BybitSocketOptions.Default = options; + BybitSocketOptions.Default = ApplyOptionsDelegate(optionsDelegate); } /// diff --git a/ByBit.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/ByBit.Net/ExtensionMethods/ServiceCollectionExtensions.cs index 0d37d7de..cb2a93d1 100644 --- a/ByBit.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/ByBit.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -7,6 +7,9 @@ using CryptoExchange.Net; using CryptoExchange.Net.Clients; using CryptoExchange.Net.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Net; using System.Net.Http; @@ -19,45 +22,112 @@ namespace Microsoft.Extensions.DependencyInjection public static class ServiceCollectionExtensions { /// - /// Add the IBybitClient and IBybitSocketClient to the sevice collection so they can be injected + /// Add services such as the IBybitRestClient and IBybitSocketClient. 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 IBybitSocketClient for the service collection. Defaults to Singleton. + /// The configuration(section) containing the options /// public static IServiceCollection AddBybit( this IServiceCollection services, - Action? defaultRestOptionsDelegate = null, - Action? defaultSocketOptionsDelegate = null, - ServiceLifetime? socketClientLifeTime = null) + IConfiguration configuration) { - var restOptions = BybitRestOptions.Default.Copy(); + var options = new BybitOptions(); + // Reset environment so we know if theyre overriden + options.Rest.Environment = null!; + options.Socket.Environment = null!; + configuration.Bind(options); - if (defaultRestOptionsDelegate != null) - { - defaultRestOptionsDelegate(restOptions); - BybitRestClient.SetDefaultOptions(defaultRestOptionsDelegate); - } + if (options.Rest == null || options.Socket == null) + throw new ArgumentException("Options null"); + + var restEnvName = options.Rest.Environment?.Name ?? options.Environment?.Name ?? BybitEnvironment.Live.Name; + var socketEnvName = options.Socket.Environment?.Name ?? options.Environment?.Name ?? BybitEnvironment.Live.Name; + options.Rest.Environment = BybitEnvironment.GetEnvironmentByName(restEnvName) ?? options.Rest.Environment!; + options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials; + options.Socket.Environment = BybitEnvironment.GetEnvironmentByName(socketEnvName) ?? options.Socket.Environment!; + options.Socket.ApiCredentials = options.Socket.ApiCredentials ?? options.ApiCredentials; - if (defaultSocketOptionsDelegate != null) - BybitSocketClient.SetDefaultOptions(defaultSocketOptionsDelegate); - services.AddHttpClient(options => + services.AddSingleton(x => Options.Options.Create(options.Rest)); + services.AddSingleton(x => Options.Options.Create(options.Socket)); + + return AddBybitCore(services, options.SocketClientLifeTime); + } + + /// + /// Add services such as the IBybitRestClient and IBybitSocketClient. Services will be configured based on the provided options. + /// + /// The service collection + /// Set options for the Bybit services + /// + public static IServiceCollection AddBybit( + this IServiceCollection services, + Action? optionsDelegate = null) + { + var options = new BybitOptions(); + // 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 ?? BybitEnvironment.Live; + options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials; + options.Socket.Environment = options.Socket.Environment ?? options.Environment ?? BybitEnvironment.Live; + 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 AddBybitCore(services, options.SocketClientLifeTime); + } + + /// + /// DEPRECATED; use instead + /// + public static IServiceCollection AddBybit( + this IServiceCollection services, + Action restDelegate, + Action? socketDelegate = null, + ServiceLifetime? socketClientLifeTime = null) + { + services.Configure((x) => { restDelegate?.Invoke(x); }); + services.Configure((x) => { socketDelegate?.Invoke(x); }); + + return AddBybitCore(services, socketClientLifeTime); + } + + private static IServiceCollection AddBybitCore( + 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 BybitRestClient(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(IBybitSocketClient), x => { return new BybitSocketClient(x.GetRequiredService>(), x.GetRequiredService()); }, socketClientLifeTime ?? ServiceLifetime.Singleton)); services.AddTransient(); services.AddTransient(); @@ -71,10 +141,6 @@ public static IServiceCollection AddBybit( services.RegisterSharedSocketInterfaces(x => x.GetRequiredService().V5InverseApi.SharedClient); services.RegisterSharedSocketInterfaces(x => x.GetRequiredService().V5PrivateApi.SharedClient); - if (socketClientLifeTime == null) - services.AddSingleton(); - else - services.Add(new ServiceDescriptor(typeof(IBybitSocketClient), typeof(BybitSocketClient), socketClientLifeTime.Value)); return services; } } diff --git a/ByBit.Net/Objects/Options/BybitOptions.cs b/ByBit.Net/Objects/Options/BybitOptions.cs new file mode 100644 index 00000000..3071197f --- /dev/null +++ b/ByBit.Net/Objects/Options/BybitOptions.cs @@ -0,0 +1,39 @@ +using CryptoExchange.Net.Authentication; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bybit.Net.Objects.Options +{ + /// + /// Bybit options + /// + public class BybitOptions + { + /// + /// Rest client options + /// + public BybitRestOptions Rest { get; set; } = new BybitRestOptions(); + + /// + /// Socket client options + /// + public BybitSocketOptions Socket { get; set; } = new BybitSocketOptions(); + + /// + /// Trade environment. Contains info about URL's to use to connect to the API. Use `BybitEnvironment` to swap environment, for example `Environment = BybitEnvironment.Live` + /// + public BybitEnvironment? Environment { get; set; } + + /// + /// The api credentials used for signing requests. + /// + public ApiCredentials? ApiCredentials { get; set; } + + /// + /// The DI service lifetime for the IBybitSocketClient + /// + public ServiceLifetime? SocketClientLifeTime { get; set; } + } +} diff --git a/ByBit.Net/Objects/Options/BybitRestOptions.cs b/ByBit.Net/Objects/Options/BybitRestOptions.cs index c6a79e63..721e6648 100644 --- a/ByBit.Net/Objects/Options/BybitRestOptions.cs +++ b/ByBit.Net/Objects/Options/BybitRestOptions.cs @@ -12,11 +12,19 @@ public class BybitRestOptions : RestExchangeOptions /// Default options for the rest client /// - public static BybitRestOptions Default { get; set; } = new BybitRestOptions + internal static BybitRestOptions Default { get; set; } = new BybitRestOptions { Environment = BybitEnvironment.Live }; + /// + /// ctor + /// + public BybitRestOptions() + { + Default?.Set(this); + } + /// /// A referer, will be sent in the Referer header /// @@ -47,16 +55,16 @@ public class BybitRestOptions : RestExchangeOptions public RestApiOptions V5Options { get; private set; } = new RestApiOptions(); - internal BybitRestOptions Copy() + internal BybitRestOptions Set(BybitRestOptions targetOptions) { - var options = Copy(); - options.Referer = Referer; - options.ReceiveWindow = ReceiveWindow; - options.SpotOptions = SpotOptions.Copy(); - options.CopyTradingOptions = CopyTradingOptions.Copy(); - options.DerivativesOptions = DerivativesOptions.Copy(); - options.V5Options = V5Options.Copy(); - return options; + targetOptions = base.Set(targetOptions); + targetOptions.Referer = Referer; + targetOptions.ReceiveWindow = ReceiveWindow; + targetOptions.SpotOptions = SpotOptions.Set(targetOptions.SpotOptions); + targetOptions.CopyTradingOptions = CopyTradingOptions.Set(targetOptions.CopyTradingOptions); + targetOptions.DerivativesOptions = DerivativesOptions.Set(targetOptions.DerivativesOptions); + targetOptions.V5Options = V5Options.Set(targetOptions.V5Options); + return targetOptions; } } } diff --git a/ByBit.Net/Objects/Options/BybitSocketApiOptions.cs b/ByBit.Net/Objects/Options/BybitSocketApiOptions.cs index 1eb7c01b..4f1e6754 100644 --- a/ByBit.Net/Objects/Options/BybitSocketApiOptions.cs +++ b/ByBit.Net/Objects/Options/BybitSocketApiOptions.cs @@ -13,11 +13,11 @@ public class BybitSocketApiOptions : SocketApiOptions /// public TimeSpan PingInterval { get; set; } = TimeSpan.FromSeconds(20); - internal BybitSocketApiOptions Copy() + internal BybitSocketApiOptions Set(BybitSocketApiOptions targetOptions) { - var result = Copy(); - result.PingInterval = PingInterval; - return result; + targetOptions = base.Set(targetOptions); + targetOptions.PingInterval = PingInterval; + return targetOptions; } } } diff --git a/ByBit.Net/Objects/Options/BybitSocketOptions.cs b/ByBit.Net/Objects/Options/BybitSocketOptions.cs index 9cf1ea5c..fb037ea9 100644 --- a/ByBit.Net/Objects/Options/BybitSocketOptions.cs +++ b/ByBit.Net/Objects/Options/BybitSocketOptions.cs @@ -18,6 +18,14 @@ public class BybitSocketOptions : SocketExchangeOptions SocketNoDataTimeout = TimeSpan.FromSeconds(30) }; + /// + /// ctor + /// + public BybitSocketOptions() + { + Default?.Set(this); + } + /// /// A referer, will be sent in the Referer header /// @@ -44,16 +52,16 @@ public class BybitSocketOptions : SocketExchangeOptions /// public BybitSocketApiOptions V5Options { get; private set; } = new BybitSocketApiOptions(); - internal BybitSocketOptions Copy() + internal BybitSocketOptions Set(BybitSocketOptions targetOptions) { - var options = Copy(); - options.Referer = Referer; - options.SpotV3Options = SpotV3Options.Copy(); - options.DerivativesPublicOptions = DerivativesPublicOptions.Copy(); - options.UnifiedMarginOptions = UnifiedMarginOptions.Copy(); - options.ContractOptions = ContractOptions.Copy(); - options.V5Options = V5Options.Copy(); - return options; + targetOptions = base.Set(targetOptions); + targetOptions.Referer = Referer; + targetOptions.SpotV3Options = SpotV3Options.Set(targetOptions.SpotV3Options); + targetOptions.DerivativesPublicOptions = DerivativesPublicOptions.Set(targetOptions.DerivativesPublicOptions); + targetOptions.UnifiedMarginOptions = UnifiedMarginOptions.Set(targetOptions.UnifiedMarginOptions); + targetOptions.ContractOptions = ContractOptions.Set(targetOptions.ContractOptions); + targetOptions.V5Options = V5Options.Set(targetOptions.V5Options); + return targetOptions; } } } diff --git a/Bybit.UnitTests/BybitRestClientTests.cs b/Bybit.UnitTests/BybitRestClientTests.cs index 84be6e62..7f2dc7a3 100644 --- a/Bybit.UnitTests/BybitRestClientTests.cs +++ b/Bybit.UnitTests/BybitRestClientTests.cs @@ -13,6 +13,10 @@ using System.Text; using System.Threading.Tasks; using CryptoExchange.Net.Converters.JsonNet; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using CryptoExchange.Net.Objects; +using Bybit.Net.Interfaces.Clients; namespace Bybit.UnitTests { @@ -129,5 +133,105 @@ public void CheckSignatureExample2() true, false); } + + [Test] + [TestCase(TradeEnvironmentNames.Live, "https://api.bybit.com")] + [TestCase("", "https://api.bybit.com")] + public void TestConstructorEnvironments(string environmentName, string expected) + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Bybit:Environment:Name", environmentName }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddBybit(configuration.GetSection("Bybit")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.V5Api.BaseAddress; + + Assert.That(address, Is.EqualTo(expected)); + } + + [Test] + public void TestConstructorNullEnvironment() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Bybit", null }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddBybit(configuration.GetSection("Bybit")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.V5Api.BaseAddress; + + Assert.That(address, Is.EqualTo("https://api.bybit.com")); + } + + [Test] + public void TestConstructorApiOverwriteEnvironment() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "Bybit:Environment:Name", "test" }, + { "Bybit:Rest:Environment:Name", "live" }, + }).Build(); + + var collection = new ServiceCollection(); + collection.AddBybit(configuration.GetSection("Bybit")); + var provider = collection.BuildServiceProvider(); + + var client = provider.GetRequiredService(); + + var address = client.V5Api.BaseAddress; + + Assert.That(address, Is.EqualTo("https://api.bybit.com")); + } + + [Test] + public void TestConstructorConfiguration() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + { "ApiCredentials:Key", "123" }, + { "ApiCredentials:Secret", "456" }, + { "ApiCredentials:Memo", "000" }, + { "Socket:ApiCredentials:Key", "456" }, + { "Socket:ApiCredentials:Secret", "789" }, + { "Socket:ApiCredentials:Memo", "xxx" }, + { "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.AddBybit(configuration); + var provider = collection.BuildServiceProvider(); + + var restClient = provider.GetRequiredService(); + var socketClient = provider.GetRequiredService(); + + Assert.That(((BaseApiClient)restClient.V5Api).OutputOriginalData, Is.True); + Assert.That(((BaseApiClient)socketClient.V5SpotApi).OutputOriginalData, Is.False); + Assert.That(((BaseApiClient)restClient.V5Api).AuthenticationProvider.ApiKey, Is.EqualTo("123")); + Assert.That(((BaseApiClient)socketClient.V5SpotApi).AuthenticationProvider.ApiKey, Is.EqualTo("456")); + Assert.That(((BaseApiClient)restClient.V5Api).ClientOptions.Proxy.Host, Is.EqualTo("host")); + Assert.That(((BaseApiClient)restClient.V5Api).ClientOptions.Proxy.Port, Is.EqualTo(80)); + Assert.That(((BaseApiClient)socketClient.V5SpotApi).ClientOptions.Proxy.Host, Is.EqualTo("host2")); + Assert.That(((BaseApiClient)socketClient.V5SpotApi).ClientOptions.Proxy.Port, Is.EqualTo(81)); + } } } diff --git a/Bybit.UnitTests/BybitRestIntegrationTests.cs b/Bybit.UnitTests/BybitRestIntegrationTests.cs index abaa2fed..6089d68f 100644 --- a/Bybit.UnitTests/BybitRestIntegrationTests.cs +++ b/Bybit.UnitTests/BybitRestIntegrationTests.cs @@ -3,6 +3,7 @@ using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Testing; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using NUnit.Framework; using System; using System.Collections.Generic; @@ -27,11 +28,11 @@ public override BybitRestClient GetClient(ILoggerFactory loggerFactory) var sec = Environment.GetEnvironmentVariable("APISECRET"); Authenticated = key != null && sec != null; - return new BybitRestClient(null, loggerFactory, opts => + return new BybitRestClient(null, loggerFactory, Options.Create(new Objects.Options.BybitRestOptions { - opts.OutputOriginalData = true; - opts.ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null; - }); + OutputOriginalData = true, + ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null + })); } [Test] diff --git a/docs/index.html b/docs/index.html index 6030be09..159b3c60 100644 --- a/docs/index.html +++ b/docs/index.html @@ -188,8 +188,14 @@

API Access

Bybit.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.AddBybit(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.AddBybit(builder.Configuration.GetSection("Bybit"));
+		  
+// OR
+		  
+ builder.Services.AddBybit(options => {
+  // Configure options in code
   options.ApiCredentials = new ApiCredentials("APIKEY", "APISECRET");
 });