From f59fb2543f313f04e11f7c0ab8457d77fde2e85b Mon Sep 17 00:00:00 2001 From: JKorf Date: Wed, 24 Jul 2024 14:05:57 +0200 Subject: [PATCH 1/4] Added integration tests, removed some unnecessary parameters --- ByBit.Net/Bybit.Net.csproj | 4 +- .../Clients/V5/BybitRestClientApiAccount.cs | 4 +- .../V5/BybitRestClientApiExchangeData.cs | 12 +- .../Clients/V5/BybitRestClientApiTrading.cs | 8 +- .../V5/IBybitRestClientApiExchangeData.cs | 3 +- .../Clients/V5/IBybitRestClientApiTrading.cs | 3 +- Bybit.UnitTests/BybitRestIntegrationTests.cs | 116 ++++++++++++++++++ Bybit.UnitTests/RestRequestTests.cs | 4 +- 8 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 Bybit.UnitTests/BybitRestIntegrationTests.cs diff --git a/ByBit.Net/Bybit.Net.csproj b/ByBit.Net/Bybit.Net.csproj index 2c430015..220e81f8 100644 --- a/ByBit.Net/Bybit.Net.csproj +++ b/ByBit.Net/Bybit.Net.csproj @@ -52,6 +52,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + \ No newline at end of file diff --git a/ByBit.Net/Clients/V5/BybitRestClientApiAccount.cs b/ByBit.Net/Clients/V5/BybitRestClientApiAccount.cs index f3f321c0..a97b0254 100644 --- a/ByBit.Net/Clients/V5/BybitRestClientApiAccount.cs +++ b/ByBit.Net/Clients/V5/BybitRestClientApiAccount.cs @@ -304,7 +304,7 @@ public async Task>> GetFeeRateAsync( #endregion - #region Get Account Info + #region Get Margin Account Info /// public async Task> GetMarginAccountInfoAsync(CancellationToken ct = default) @@ -1041,7 +1041,7 @@ public async Task>> GetConvertAsset #endregion - #region Get Convert Assets + #region Get Convert Quote /// public async Task> GetConvertQuoteAsync(ConvertAccountType accountType, string fromAsset, string toAsset, decimal quantity, string? clientOrderId = null, CancellationToken ct = default) diff --git a/ByBit.Net/Clients/V5/BybitRestClientApiExchangeData.cs b/ByBit.Net/Clients/V5/BybitRestClientApiExchangeData.cs index be2813c6..207f2a3c 100644 --- a/ByBit.Net/Clients/V5/BybitRestClientApiExchangeData.cs +++ b/ByBit.Net/Clients/V5/BybitRestClientApiExchangeData.cs @@ -48,7 +48,6 @@ public async Task>> GetAnnounceme /// public async Task> GetServerTimeAsync(CancellationToken ct = default) { - // V5 doesn't have it's own server time endpoint (yet) return await _baseClient.SendRequestAsync(_baseClient.GetUrl("v5/market/time"), HttpMethod.Get, ct, null).ConfigureAwait(false); } @@ -169,7 +168,7 @@ public async Task>> GetOptionSymb #endregion - #region Get Option symbols + #region Get Linear Inverse symbols /// public async Task>> GetLinearInverseSymbolsAsync( @@ -339,17 +338,14 @@ public async Task>> GetOpenIntere #endregion - #region Get Historic Volatility + #region Get Historical Volatility /// - public async Task>> GetHistoricalVolatilityAsync(Category category, string? baseAsset = null, int? period = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, string? cursor = null, CancellationToken ct = default) + public async Task>> GetHistoricalVolatilityAsync(string? baseAsset = null, int? period = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, string? cursor = null, CancellationToken ct = default) { - if (category != Category.Option) - throw new ArgumentException("Invalid category; should be Option"); - var parameters = new Dictionary() { - { "category", EnumConverter.GetString(category) }, + { "category", EnumConverter.GetString(Category.Option) }, }; parameters.AddOptionalParameter("startTime", DateTimeConverter.ConvertToMilliseconds(startTime)); parameters.AddOptionalParameter("endTime", DateTimeConverter.ConvertToMilliseconds(endTime)); diff --git a/ByBit.Net/Clients/V5/BybitRestClientApiTrading.cs b/ByBit.Net/Clients/V5/BybitRestClientApiTrading.cs index 07ff0bc8..86ee1d71 100644 --- a/ByBit.Net/Clients/V5/BybitRestClientApiTrading.cs +++ b/ByBit.Net/Clients/V5/BybitRestClientApiTrading.cs @@ -451,17 +451,13 @@ public async Task>> CancelAllOrderAsyn /// public async Task> GetBorrowQuotaAsync( - Category category, string symbol, OrderSide side, CancellationToken ct = default) { - if (category != Category.Spot) - throw new ArgumentException("Category should be spot"); - var parameters = new Dictionary() { - { "category", EnumConverter.GetString(category) }, + { "category", EnumConverter.GetString(Category.Spot) }, { "symbol", symbol }, { "side", EnumConverter.GetString(side) }, }; @@ -565,7 +561,7 @@ public async Task>> GetPositionsAsync #endregion - #region Get Positions + #region Confirm Risk Limit /// public async Task ConfirmRiskLimitAsync( diff --git a/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiExchangeData.cs b/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiExchangeData.cs index 129f49e3..bf4b9355 100644 --- a/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiExchangeData.cs +++ b/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiExchangeData.cs @@ -64,7 +64,6 @@ public interface IBybitRestClientApiExchangeData /// Get historical volatility /// /// - /// Category /// Filter by base asset /// Period /// Fitler by start time @@ -73,7 +72,7 @@ public interface IBybitRestClientApiExchangeData /// Pagination cursor /// Cancellation token /// - Task>> GetHistoricalVolatilityAsync(Category category, string? baseAsset = null, int? period = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, string? cursor = null, CancellationToken ct = default); + Task>> GetHistoricalVolatilityAsync(string? baseAsset = null, int? period = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, string? cursor = null, CancellationToken ct = default); /// /// Get index price klines diff --git a/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiTrading.cs b/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiTrading.cs index 7f57dd5d..a2f644b9 100644 --- a/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiTrading.cs +++ b/ByBit.Net/Interfaces/Clients/V5/IBybitRestClientApiTrading.cs @@ -98,12 +98,11 @@ Task> EditOrderAsync( /// Get spot borrow quota /// /// - /// Category /// Symbol /// Side /// Cancellation token /// - Task> GetBorrowQuotaAsync(Category category, string symbol, OrderSide side, CancellationToken ct = default); + Task> GetBorrowQuotaAsync(string symbol, OrderSide side, CancellationToken ct = default); /// /// Get delivery history diff --git a/Bybit.UnitTests/BybitRestIntegrationTests.cs b/Bybit.UnitTests/BybitRestIntegrationTests.cs new file mode 100644 index 00000000..a34c75be --- /dev/null +++ b/Bybit.UnitTests/BybitRestIntegrationTests.cs @@ -0,0 +1,116 @@ +using Bybit.Net.Clients; +using Bybit.Net.Objects; +using CryptoExchange.Net.Authentication; +using CryptoExchange.Net.Testing; +using Microsoft.Extensions.Logging; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Bybit.Net.UnitTests +{ + [NonParallelizable] + internal class BybitRestIntegrationTests : RestIntergrationTest + { + public override bool Run { get; set; } + + public BybitRestIntegrationTests() + { + } + + public override BybitRestClient GetClient(ILoggerFactory loggerFactory) + { + var key = Environment.GetEnvironmentVariable("APIKEY"); + var sec = Environment.GetEnvironmentVariable("APISECRET"); + + Authenticated = key != null && sec != null; + return new BybitRestClient(null, loggerFactory, opts => + { + opts.OutputOriginalData = true; + opts.ApiCredentials = Authenticated ? new ApiCredentials(key, sec) : null; + }); + } + + [Test] + public async Task TestErrorResponseParsing() + { + if (!ShouldRun()) + return; + + var result = await CreateClient().V5Api.ExchangeData.GetSpotTickersAsync("TST_TST", default); + + Assert.That(result.Success, Is.False); + Assert.That(result.Error.Code, Is.EqualTo(10001)); + } + + [Test] + public async Task TestV5Account() + { + await RunAndCheckResult(client => client.V5Api.Account.GetBalancesAsync(Enums.AccountType.Unified, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetBorrowHistoryAsync(default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetCollateralInfoAsync(default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetAssetGreeksAsync(default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetFeeRateAsync(Enums.Category.Spot, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetMarginAccountInfoAsync(default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetTransactionHistoryAsync(default, default, default, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetAssetInfoAsync(default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetWithdrawalsAsync(default, default, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetDelayedWithdrawQuantityAsync("ETH", default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetApiKeyInfoAsync(default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetAccountTypesAsync(default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetSpotMarginStatusAndLeverageAsync(default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetSpotMarginDataAsync(default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetConvertAssetsAsync(Enums.ConvertAccountType.ConvertUta, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Account.GetConvertHistoryAsync(Enums.ConvertAccountType.ConvertUta, default, default, default), true); + + // Only available to broker account + //await RunAndCheckResult(client => client.V5Api.Account.GetBrokerAccountInfoAsync(default), true); + //await RunAndCheckResult(client => client.V5Api.Account.GetBrokerEarningsAsync(default, default, default, default, default, default, default), true); + } + + [Test] + public async Task TestV5ExchangeData() + { + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetServerTimeAsync(default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetKlinesAsync(Enums.Category.Spot, "ETHUSDT", Enums.KlineInterval.OneDay, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetMarkPriceKlinesAsync(Enums.Category.Linear, "ETHUSDT", Enums.KlineInterval.OneDay, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetIndexPriceKlinesAsync(Enums.Category.Linear, "ETHUSDT", Enums.KlineInterval.OneDay, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetPremiumIndexPriceKlinesAsync(Enums.Category.Linear, "ETHUSDT", Enums.KlineInterval.OneDay, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetSpotSymbolsAsync(default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetOptionSymbolsAsync(default, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetLinearInverseSymbolsAsync(default, default, default, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetOrderbookAsync(Enums.Category.Spot, "ETHUSDT", default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetSpotTickersAsync(default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetOptionTickersAsync(default, "BTC", default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetLinearInverseTickersAsync(Enums.Category.Linear, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetFundingRateHistoryAsync(Enums.Category.Linear, "ETHUSDT", default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetTradeHistoryAsync(Enums.Category.Linear, "ETHUSDT", default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetOpenInterestAsync(Enums.Category.Linear, "ETHUSDT", Enums.OpenInterestInterval.OneDay, default, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetHistoricalVolatilityAsync(default, default, default, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetInsuranceAsync("ETH", default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetRiskLimitAsync(Enums.Category.Linear, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetDeliveryPriceAsync(Enums.Category.Linear, default, default, default, default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetLeverageTokensAsync(default, default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetLeverageTokenMarketAsync("BTC3L", default), false); + await RunAndCheckResult(client => client.V5Api.ExchangeData.GetLongShortRatioAsync(Enums.Category.Linear, "ETHUSDT", Enums.DataPeriod.OneDay, default, default), false); + } + + [Test] + public async Task TestV5Trading() + { + await RunAndCheckResult(client => client.V5Api.Trading.GetOrdersAsync(Enums.Category.Spot, default, default, default, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetOrderHistoryAsync(Enums.Category.Spot, default, default, default, default, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetBorrowQuotaAsync("ETHUSDT", Enums.OrderSide.Buy, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetDisconnectCancelAllConfigAsync(default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetUserTradesAsync(Enums.Category.Spot, default, default, default, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetPositionsAsync(Enums.Category.Linear, default, default, "USDT", default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetAssetExchangeHistoryAsync(default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetDeliveryHistoryAsync(Enums.Category.Linear, default, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetSettlementHistoryAsync(Enums.Category.Linear, default, default, default, default, default, default), true); + await RunAndCheckResult(client => client.V5Api.Trading.GetClosedProfitLossAsync(Enums.Category.Linear, default, default, default, default, default, default), true); + } + } +} diff --git a/Bybit.UnitTests/RestRequestTests.cs b/Bybit.UnitTests/RestRequestTests.cs index 6cdf9e52..12998c6c 100644 --- a/Bybit.UnitTests/RestRequestTests.cs +++ b/Bybit.UnitTests/RestRequestTests.cs @@ -88,7 +88,7 @@ public async Task ValidateSpotExchangeDataCalls() await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetFundingRateHistoryAsync(Enums.Category.Inverse, "ETHUSDT"), "GetFundingRateHistory"); await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetTradeHistoryAsync(Enums.Category.Inverse, "ETHUSDT"), "GetTradeHistory"); await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetOpenInterestAsync(Enums.Category.Inverse, "ETHUSDT", Enums.OpenInterestInterval.OneDay), "GetOpenInterest"); - await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetHistoricalVolatilityAsync(Enums.Category.Option, "ETHUSDT"), "GetHistoricalVolatility"); + await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetHistoricalVolatilityAsync("ETHUSDT"), "GetHistoricalVolatility"); await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetInsuranceAsync(), "GetInsurance"); await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetRiskLimitAsync(Enums.Category.Inverse), "GetRiskLimit"); await tester.ValidateAsync(client => client.V5Api.ExchangeData.GetDeliveryPriceAsync(Enums.Category.Inverse), "GetDeliveryPrice"); @@ -115,7 +115,7 @@ public async Task ValidateSpotTradingCalls() await tester.ValidateAsync(client => client.V5Api.Trading.GetOrdersAsync(Enums.Category.Option, "ETHUSDT"), "GetOrders"); await tester.ValidateAsync(client => client.V5Api.Trading.CancelAllOrderAsync(Enums.Category.Option, "ETHUSDT"), "CancelAllOrder", ignoreProperties: new List { "success" }); await tester.ValidateAsync(client => client.V5Api.Trading.GetOrderHistoryAsync(Enums.Category.Option, "ETHUSDT"), "GetOrderHistory"); - await tester.ValidateAsync(client => client.V5Api.Trading.GetBorrowQuotaAsync(Enums.Category.Spot, "ETHUSDT", Enums.OrderSide.Buy), "GetBorrowQuota"); + await tester.ValidateAsync(client => client.V5Api.Trading.GetBorrowQuotaAsync("ETHUSDT", Enums.OrderSide.Buy), "GetBorrowQuota"); await tester.ValidateAsync(client => client.V5Api.Trading.SetDisconnectCancelAllAsync(10), "SetDisconnectCancelAll"); await tester.ValidateAsync(client => client.V5Api.Trading.GetUserTradesAsync(Enums.Category.Option), "GetUserTrades"); await tester.ValidateAsync(client => client.V5Api.Trading.GetPositionsAsync(Enums.Category.Option), "GetPositions", ignoreProperties: new List { "mmrSysUpdateTime" }); From b714961c03ddb75a3a631b61936b46bf2e8988e7 Mon Sep 17 00:00:00 2001 From: JKorf Date: Wed, 24 Jul 2024 14:07:11 +0200 Subject: [PATCH 2/4] Added periodic integration test workflow --- .github/workflows/tests.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..71a3b06a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,25 @@ +name: Integration Testing + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + - name: Set GitHub package source + run: dotnet nuget add source --username JKorf --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/JKorf/index.json" + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal -e INTEGRATION=1 From b6ee31f7cbae26f130324eb71dab0cdc3857c381 Mon Sep 17 00:00:00 2001 From: JKorf Date: Thu, 25 Jul 2024 18:48:49 +0200 Subject: [PATCH 3/4] Fixed v5Api.Account.GetCollateralInfoAsync() deserialization --- ByBit.Net/Objects/Models/V5/BybitCollateralInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ByBit.Net/Objects/Models/V5/BybitCollateralInfo.cs b/ByBit.Net/Objects/Models/V5/BybitCollateralInfo.cs index 0ac5b790..4da4212c 100644 --- a/ByBit.Net/Objects/Models/V5/BybitCollateralInfo.cs +++ b/ByBit.Net/Objects/Models/V5/BybitCollateralInfo.cs @@ -66,6 +66,6 @@ public record BybitCollateralInfo /// Borrow usage rate /// [JsonProperty("borrowUsageRate")] - public decimal BorrowUsageRate { get; set; } + public decimal? BorrowUsageRate { get; set; } } } From e9d99488ecabb4d0effbefcd08fcb3039555bd2f Mon Sep 17 00:00:00 2001 From: JKorf Date: Fri, 26 Jul 2024 14:40:56 +0200 Subject: [PATCH 4/4] Updated CryptoExchange.Net --- ByBit.Net/Bybit.Net.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ByBit.Net/Bybit.Net.csproj b/ByBit.Net/Bybit.Net.csproj index 220e81f8..9ab1503e 100644 --- a/ByBit.Net/Bybit.Net.csproj +++ b/ByBit.Net/Bybit.Net.csproj @@ -52,8 +52,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + \ No newline at end of file