From 33dbfe9b7dfe5a914c4b8d1fa3db55b79b1a0639 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Fri, 5 Jun 2026 14:59:48 +0200 Subject: [PATCH 01/15] wip --- Examples/ai-friendly/05-error-handling.cs | 12 +- Examples/ai-friendly/README.md | 2 +- WhiteBit.Net.UnitTests/RestRequestTests.cs | 2 +- .../WhiteBitRestClientTests.cs | 5 +- .../Clients/V4Api/WhiteBitRestClientV4Api.cs | 31 +- .../V4Api/WhiteBitRestClientV4ApiAccount.cs | 190 ++-- .../V4Api/WhiteBitRestClientV4ApiCodes.cs | 32 +- ...hiteBitRestClientV4ApiCollateralTrading.cs | 92 +- .../V4Api/WhiteBitRestClientV4ApiConvert.cs | 28 +- .../WhiteBitRestClientV4ApiExchangeData.cs | 74 +- .../V4Api/WhiteBitRestClientV4ApiShared.cs | 852 +++++++++--------- .../WhiteBitRestClientV4ApiSubAccount.cs | 66 +- .../V4Api/WhiteBitRestClientV4ApiTrading.cs | 158 ++-- .../V4Api/WhiteBitSocketClientV4Api.cs | 97 +- .../V4Api/WhiteBitSocketClientV4ApiShared.cs | 130 ++- .../WhiteBitSourceGenerationContext.cs | 2 + .../V4Api/IWhiteBitRestClientV4ApiAccount.cs | 38 +- .../V4Api/IWhiteBitRestClientV4ApiCodes.cs | 8 +- ...hiteBitRestClientV4ApiCollateralTrading.cs | 16 +- .../V4Api/IWhiteBitRestClientV4ApiConvert.cs | 6 +- .../IWhiteBitRestClientV4ApiExchangeData.cs | 22 +- .../IWhiteBitRestClientV4ApiSubAccount.cs | 18 +- .../V4Api/IWhiteBitRestClientV4ApiTrading.cs | 24 +- .../V4Api/IWhiteBitSocketClientV4Api.cs | 58 +- .../Subscriptions/WhiteBitBookSubscription.cs | 4 +- .../WhiteBitClosedOrderSubscription.cs | 4 +- .../WhiteBitKlineSubscription.cs | 4 +- .../WhiteBitMarginBalanceSubscription.cs | 4 +- .../WhiteBitOpenOrderSubscription.cs | 8 +- .../WhiteBitSpotBalanceSubscription.cs | 4 +- .../Subscriptions/WhiteBitSubscription.cs | 8 +- .../WhiteBitUserTradeSubscription.cs | 4 +- WhiteBit.Net/Objects/Sockets/WhiteBitQuery.cs | 6 +- .../WhiteBitV4SymbolOrderBook.cs | 12 +- WhiteBit.Net/WhiteBit.Net.csproj | 6 +- .../WhiteBitAuthenticationProvider.cs | 2 +- WhiteBit.Net/WhiteBitExchange.cs | 5 + 37 files changed, 1012 insertions(+), 1022 deletions(-) diff --git a/Examples/ai-friendly/05-error-handling.cs b/Examples/ai-friendly/05-error-handling.cs index 8d5ce63..43a5e08 100644 --- a/Examples/ai-friendly/05-error-handling.cs +++ b/Examples/ai-friendly/05-error-handling.cs @@ -1,6 +1,6 @@ // 05-error-handling.cs // -// Demonstrates: WebCallResult patterns, retry logic, common WhiteBit routing +// Demonstrates: HttpResult patterns, retry logic, common WhiteBit routing // and validation scenarios. // // Setup: dotnet add package WhiteBit.Net @@ -16,7 +16,7 @@ }); // ---- 1. THE BASIC PATTERN ---- -// Every REST method returns WebCallResult or WebCallResult. +// Every REST method returns HttpResult or HttpResult. // .Success is true/false. .Data is valid only when .Success is true. // .Error contains structured error info when .Success is false. @@ -39,11 +39,11 @@ // Retry only on transient errors: network blips, temporary server errors, rate limits. // Do not retry validation errors, permission errors, or insufficient balance. -async Task> WithRetry( - Func>> call, +async Task> WithRetry( + Func>> call, int maxAttempts = 3) { - WebCallResult last = default!; + HttpResult last = default!; for (var attempt = 1; attempt <= maxAttempts; attempt++) { last = await call(); @@ -134,7 +134,7 @@ async Task> WithRetry( } // ---- 5. EXCEPTIONS VS ERROR RESULTS ---- -// WhiteBit.Net returns exchange and network errors via WebCallResult.Error. +// WhiteBit.Net returns exchange and network errors via HttpResult.Error. // Exceptions are usually for misconfiguration, disposal, cancellation, or programming errors. // Common variations: diff --git a/Examples/ai-friendly/README.md b/Examples/ai-friendly/README.md index 79b93c3..6946865 100644 --- a/Examples/ai-friendly/README.md +++ b/Examples/ai-friendly/README.md @@ -15,7 +15,7 @@ These examples are optimized for AI coding assistants and quick onboarding. Each | `02-collateral-futures.cs` | Collateral/futures leverage, market order, open position retrieval, reduce-only close | | `03-websocket.cs` | Ticker updates, kline updates, private order and balance streams, proper teardown | | `04-multi-exchange.cs` | `CryptoExchange.Net.SharedApis` pattern for exchange-agnostic code | -| `05-error-handling.cs` | `WebCallResult` patterns, retry, common validation and routing mistakes | +| `05-error-handling.cs` | `HttpResult` patterns, retry, common validation and routing mistakes | ## Running diff --git a/WhiteBit.Net.UnitTests/RestRequestTests.cs b/WhiteBit.Net.UnitTests/RestRequestTests.cs index 78a1ef3..f4e2c63 100644 --- a/WhiteBit.Net.UnitTests/RestRequestTests.cs +++ b/WhiteBit.Net.UnitTests/RestRequestTests.cs @@ -101,7 +101,7 @@ public async Task ValidateV4CollateralTradingCalls() await tester.ValidateAsync(client => client.V4Api.CollateralTrading.CancelOcoOrderAsync("123", 123), "CancelOcoOrder"); } - private bool IsAuthenticated(WebCallResult result) + private bool IsAuthenticated(IHttpResult result) { return result.RequestHeaders?.Any(x => x.Key == "X-TXC-SIGNATURE") == true; } diff --git a/WhiteBit.Net.UnitTests/WhiteBitRestClientTests.cs b/WhiteBit.Net.UnitTests/WhiteBitRestClientTests.cs index c517d62..5a66510 100644 --- a/WhiteBit.Net.UnitTests/WhiteBitRestClientTests.cs +++ b/WhiteBit.Net.UnitTests/WhiteBitRestClientTests.cs @@ -34,13 +34,12 @@ public void CheckSignatureExample1() return headers["X-TXC-SIGNATURE"].ToString(); }, "e164832d38f40692420d44786f8dbc98a46af816ed6c30dbc5d6178e71e63f491a4b6dbfea93c887f50c570d4b403dd61fe2432908754f49f904d06c51c84680", - new Dictionary + new Parameters(WhiteBitExchange._parameterSerializationSettings) { { "market", "ETH_USDT" }, }, DateTimeConverter.ParseFromDouble(1499827320559), - true, - false); + true); } [Test] diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs index 1a9f055..00dc79c 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs @@ -54,7 +54,7 @@ internal partial class WhiteBitRestClientV4Api : RestApiClient new WhiteBitAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new WhiteBitNonceProvider()); - internal Task SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) + internal Task SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); - internal async Task SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) + internal async Task SendToAddressAsync(string baseAddress, RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) { - var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); + var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); + if (!result.Success && result.Error is DeserializeError) + return new HttpResult(result.Exchange, result.Data, null) + { + ResponseStatusCode = result.ResponseStatusCode, + HttpVersion = result.HttpVersion, + ResponseHeaders = result.ResponseHeaders, + ResponseTime = result.ResponseTime, + ResponseLength = result.ResponseLength, + OriginalData = result.OriginalData, + RequestId = result.RequestId, + RequestUrl = result.RequestUrl, + RequestBody = result.RequestBody, + RequestMethod = result.RequestMethod, + RequestHeaders = result.RequestHeaders, + DataSource = result.DataSource, + }; + return result; } - internal Task> SendAsync(RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class + internal Task> SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); - internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, ParameterCollection? parameters, CancellationToken cancellationToken, int? weight = null) where T : class + internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class { var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); return result; } /// - protected override Task> GetServerTimestampAsync() + protected override Task> GetServerTimestampAsync() => ExchangeData.GetServerTimeAsync(); /// diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs index dd57fc0..275e2d0 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs @@ -26,19 +26,19 @@ internal WhiteBitRestClientV4ApiAccount(WhiteBitRestClientV4Api baseClient) #region Get Main Balances /// - public async Task> GetMainBalancesAsync(CancellationToken ct = default) + public async Task> GetMainBalancesAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data?.Values.ToArray()); } #endregion @@ -46,11 +46,11 @@ public async Task> GetMainBalancesAsync(Can #region Get Deposit Address /// - public async Task> GetDepositAddressAsync(string asset, string? network = null, CancellationToken ct = default) + public async Task> GetDepositAddressAsync(string asset, string? network = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - parameters.AddOptional("network", network); + parameters.Add("network", network); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -62,20 +62,20 @@ public async Task> GetDepositAddressAs #region Get Spot Balances /// - public async Task> GetSpotBalancesAsync(string? asset = null, CancellationToken ct = default) + public async Task> GetSpotBalancesAsync(string? asset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("ticker", asset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("ticker", asset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data?.Values.ToArray()); } #endregion @@ -83,26 +83,26 @@ public async Task> GetSpotBalancesAsync(st #region Get Fiat Deposit Address /// - public async Task> GetFiatDepositAddressAsync(string asset, string provider, decimal quantity, string clientOrderId, string? successLink = null, string? failureLink = null, string? returnLink = null, string? customerFirstName = null, string? customerLastName = null, string? customerEmail = null, string? customerAddressLine1 = null, string? customerAddressLine2 = null, string? customerCity = null, string? customerZipCode = null, string? customerCountryCode = null, CancellationToken ct = default) + public async Task> GetFiatDepositAddressAsync(string asset, string provider, decimal quantity, string clientOrderId, string? successLink = null, string? failureLink = null, string? returnLink = null, string? customerFirstName = null, string? customerLastName = null, string? customerEmail = null, string? customerAddressLine1 = null, string? customerAddressLine2 = null, string? customerCity = null, string? customerZipCode = null, string? customerCountryCode = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); parameters.Add("provider", provider); - parameters.AddString("amount", quantity); + parameters.Add("amount", quantity); parameters.Add("uniqueId", clientOrderId); - parameters.AddOptional("successLink", successLink); - parameters.AddOptional("failureLink", failureLink); - parameters.AddOptional("returnLink", returnLink); - var customer = new ParameterCollection(); - customer.AddOptional("firstName", customerFirstName); - customer.AddOptional("lastName", customerLastName); - customer.AddOptional("email", customerEmail); - var address = new ParameterCollection(); - address.AddOptional("addressLine1", customerAddressLine1); - address.AddOptional("addressLine2", customerAddressLine2); - address.AddOptional("city", customerCity); - address.AddOptional("zipCode", customerZipCode); - address.AddOptional("countryCode", customerCountryCode); + parameters.Add("successLink", successLink); + parameters.Add("failureLink", failureLink); + parameters.Add("returnLink", returnLink); + var customer = new Parameters(WhiteBitExchange._parameterSerializationSettings); + customer.Add("firstName", customerFirstName); + customer.Add("lastName", customerLastName); + customer.Add("email", customerEmail); + var address = new Parameters(WhiteBitExchange._parameterSerializationSettings); + address.Add("addressLine1", customerAddressLine1); + address.Add("addressLine2", customerAddressLine2); + address.Add("city", customerCity); + address.Add("zipCode", customerZipCode); + address.Add("countryCode", customerCountryCode); if (address.Any()) customer.Add("address", address); if (customer.Any()) @@ -119,23 +119,23 @@ public async Task> GetFiatDepositAddressAsync( #region Withdraw /// - public async Task WithdrawAsync(string asset, decimal quantity, string address, string uniqueId, bool deductFeeFromOutput, string? memo = null, string? provider = null, string? network = null, bool? partialEnable = null, string? beneficiaryFirstName = null, string? beneficiaryLastName = null, string? beneficiaryTin = null, string? beneficiaryPhone = null, string? beneficiaryEmail = null, CancellationToken ct = default) + public async Task WithdrawAsync(string asset, decimal quantity, string address, string uniqueId, bool deductFeeFromOutput, string? memo = null, string? provider = null, string? network = null, bool? partialEnable = null, string? beneficiaryFirstName = null, string? beneficiaryLastName = null, string? beneficiaryTin = null, string? beneficiaryPhone = null, string? beneficiaryEmail = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - parameters.AddString("amount", quantity); + parameters.Add("amount", quantity); parameters.Add("address", address); parameters.Add("uniqueId", uniqueId); - parameters.AddOptional("memo", memo); - parameters.AddOptional("provider", provider); - parameters.AddOptional("network", network); - parameters.AddOptional("partialEnable", partialEnable); - var beneficiary = new ParameterCollection(); - beneficiary.AddOptional("firstName", beneficiaryFirstName); - beneficiary.AddOptional("lastName", beneficiaryLastName); - beneficiary.AddOptional("tin", beneficiaryTin); - beneficiary.AddOptional("phone", beneficiaryPhone); - beneficiary.AddOptional("email", beneficiaryEmail); + parameters.Add("memo", memo); + parameters.Add("provider", provider); + parameters.Add("network", network); + parameters.Add("partialEnable", partialEnable); + var beneficiary = new Parameters(WhiteBitExchange._parameterSerializationSettings); + beneficiary.Add("firstName", beneficiaryFirstName); + beneficiary.Add("lastName", beneficiaryLastName); + beneficiary.Add("tin", beneficiaryTin); + beneficiary.Add("phone", beneficiaryPhone); + beneficiary.Add("email", beneficiaryEmail); if (beneficiary.Any()) parameters.Add("beneficiary", beneficiary); @@ -154,13 +154,13 @@ public async Task WithdrawAsync(string asset, decimal quantity, s #region Transfer /// - public async Task TransferAsync(AccountType fromAccount, AccountType toAccount, string asset, decimal quantity, CancellationToken ct = default) + public async Task TransferAsync(AccountType fromAccount, AccountType toAccount, string asset, decimal quantity, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddEnum("from", fromAccount); - parameters.AddEnum("to", toAccount); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("from", fromAccount); + parameters.Add("to", toAccount); parameters.Add("ticker", asset); - parameters.AddString("amount", quantity); + parameters.Add("amount", quantity); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -172,17 +172,17 @@ public async Task TransferAsync(AccountType fromAccount, AccountT #region Get Deposit Withdrawal History /// - public async Task> GetDepositWithdrawalHistoryAsync(TransactionType? type = null, string? asset = null, string? address = null, string? memo = null, string? addresses = null, string? uniqueId = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetDepositWithdrawalHistoryAsync(TransactionType? type = null, string? asset = null, string? address = null, string? memo = null, string? addresses = null, string? uniqueId = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptionalEnumAsInt("transactionMethod", type); - parameters.AddOptional("ticker", asset); - parameters.AddOptional("address", address); - parameters.AddOptional("memo", memo); - parameters.AddOptional("addresses", addresses); - parameters.AddOptional("uniqueId", uniqueId); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.AddAsInt("transactionMethod", type); + parameters.Add("ticker", asset); + parameters.Add("address", address); + parameters.Add("memo", memo); + parameters.Add("addresses", addresses); + parameters.Add("uniqueId", uniqueId); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(200, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -194,12 +194,12 @@ public async Task> GetDepositWithdrawa #region Create Deposit Address /// - public async Task> CreateDepositAddressAsync(string asset, string? network = null, string? addressType = null, CancellationToken ct = default) + public async Task> CreateDepositAddressAsync(string asset, string? network = null, string? addressType = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - parameters.AddOptional("network", network); - parameters.AddOptional("type", addressType); + parameters.Add("network", network); + parameters.Add("type", addressType); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/create-new-address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -211,9 +211,9 @@ public async Task> CreateDepositAddres #region Get Deposit Withdrawal Settings /// - public async Task> GetDepositWithdrawalSettingsAsync(CancellationToken ct = default) + public async Task> GetDepositWithdrawalSettingsAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -225,14 +225,14 @@ public async Task> GetDepositW #region Get Mining Reward History /// - public async Task> GetMiningRewardHistoryAsync(string? accountName = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetMiningRewardHistoryAsync(string? accountName = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("account", accountName); - parameters.AddOptionalMilliseconds("from", startTime); - parameters.AddOptionalMilliseconds("to", endTime); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("account", accountName); + parameters.Add("from", startTime); + parameters.Add("to", endTime); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/mining/rewards", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -244,10 +244,10 @@ public async Task> GetMiningRewardHistoryAs #region Get Collateral Balances /// - public async Task>> GetCollateralBalancesAsync(string? asset = null, CancellationToken ct = default) + public async Task>> GetCollateralBalancesAsync(string? asset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("ticker", asset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("ticker", asset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); @@ -259,9 +259,9 @@ public async Task>> GetCollateralBalan #region Get Collateral Balance Summary /// - public async Task> GetCollateralBalanceSummaryAsync(CancellationToken ct = default) + public async Task> GetCollateralBalanceSummaryAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/balance-summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -272,9 +272,9 @@ public async Task> GetCollateralBalan #region Get Collateral Account Summary /// - public async Task> GetCollateralAccountSummaryAsync(CancellationToken ct = default) + public async Task> GetCollateralAccountSummaryAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -286,7 +286,7 @@ public async Task> GetCollateral #region Get Account Funding History /// - public async Task> GetAccountFundingHistoryAsync( + public async Task> GetAccountFundingHistoryAsync( string symbol, DateTime? startTime = null, DateTime? endTime = null, @@ -294,15 +294,15 @@ public async Task> GetAccountFund int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings) { { "market", symbol } }; - parameters.AddOptionalSeconds("startDate", startTime); - parameters.AddOptionalSeconds("endDate", endTime); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + parameters.Add("startDate", startTime); + parameters.Add("endDate", endTime); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/funding-history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, @@ -314,9 +314,9 @@ public async Task> GetAccountFund #region Set Account Leverage /// - public async Task> SetAccountLeverageAsync(int leverage, CancellationToken ct = default) + public async Task> SetAccountLeverageAsync(int leverage, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("leverage", leverage); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/leverage", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -330,9 +330,9 @@ public async Task> SetAccountLeverageAsync(int l //#region Get Trading Fee ///// - //public async Task> GetTradingFeeAsync(string symbol, CancellationToken ct = default) + //public async Task> GetTradingFeeAsync(string symbol, CancellationToken ct = default) //{ - // var parameters = new ParameterCollection(); + // var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); // parameters.Add("market", symbol); // var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); // return await _baseClient.SendAsync(request, parameters, null, ct).ConfigureAwait(false); @@ -343,7 +343,7 @@ public async Task> SetAccountLeverageAsync(int l #region Get Trading Fees /// - public async Task> GetTradingFeesAsync(CancellationToken ct = default) + public async Task> GetTradingFeesAsync(CancellationToken ct = default) { var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); @@ -354,9 +354,9 @@ public async Task> GetTradingFeesAsync(Cancel #region Get Hedge Mode /// - public async Task> GetHedgeModeAsync(CancellationToken ct = default) + public async Task> GetHedgeModeAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/hedge-mode", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -367,9 +367,9 @@ public async Task> GetHedgeModeAsync(Cancellati #region Set Hedge Mode /// - public async Task> SetHedgeModeAsync(bool enableHedgeMode, CancellationToken ct = default) + public async Task> SetHedgeModeAsync(bool enableHedgeMode, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("hedgeMode", enableHedgeMode); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/hedge-mode/update", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -379,11 +379,11 @@ public async Task> SetHedgeModeAsync(bool enabl #endregion - internal async Task> GetWebsocketTokenAsync(CancellationToken ct = default) + internal async Task> GetWebsocketTokenAsync(CancellationToken ct = default) { var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/profile/websocket_token", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); - return result.As(result.Data?.Token); + return HttpResult.Ok(result, result.Data?.Token); } } } diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs index 02e4e9d..275f3dc 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs @@ -23,13 +23,13 @@ internal WhiteBitRestClientV4ApiCodes(WhiteBitRestClientV4Api baseClient) #region Create Code /// - public async Task> CreateCodeAsync(string asset, decimal quantity, string? passphrase = null, string? description = null, CancellationToken ct = default) + public async Task> CreateCodeAsync(string asset, decimal quantity, string? passphrase = null, string? description = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - parameters.AddString("amount", quantity); - parameters.AddOptional("passphrase", passphrase); - parameters.AddOptional("description", description); + parameters.Add("amount", quantity); + parameters.Add("passphrase", passphrase); + parameters.Add("description", description); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -41,11 +41,11 @@ public async Task> CreateCodeAsync(string asset, dec #region Apply Code /// - public async Task> ApplyCodeAsync(string code, string? passphrase = null, CancellationToken ct = default) + public async Task> ApplyCodeAsync(string code, string? passphrase = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("code", code); - parameters.AddOptional("passphrase", passphrase); + parameters.Add("passphrase", passphrase); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes/apply", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(60, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -57,11 +57,11 @@ public async Task> ApplyCodeAsync(string code, #region Get Created Codes /// - public async Task> GetCreatedCodesAsync(int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetCreatedCodesAsync(int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/main-account/codes/my", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -73,11 +73,11 @@ public async Task> GetCreatedCodesAsync(in #region Get Code History /// - public async Task> GetCodeHistoryAsync(int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetCodeHistoryAsync(int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs index 1cc7bb0..0df5567 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs @@ -28,7 +28,7 @@ internal WhiteBitRestClientV4ApiCollateralTrading(ILogger logger, WhiteBitRestCl #region Place Order /// - public async Task> PlaceOrderAsync( + public async Task> PlaceOrderAsync( string symbol, OrderSide side, NewOrderType type, @@ -46,21 +46,21 @@ public async Task> PlaceOrderAsync( bool? reduceOnly = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.AddEnum("side", side); - parameters.AddOptionalString("amount", quantity); - parameters.AddOptionalString("price", price); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptional("postOnly", postOnly); - parameters.AddOptional("ioc", immediateOrCancel); - parameters.AddOptional("bboRole", bboRole); - parameters.AddOptionalString("activation_price", triggerPrice); - parameters.AddOptionalString("stopLoss", stopLossPrice); - parameters.AddOptionalString("takeProfit", takeProfitPrice); - parameters.AddOptionalEnum("stp", stpMode); - parameters.AddOptionalEnum("positionSide", positionSide); - parameters.AddOptional("reduceOnly", reduceOnly); + parameters.Add("side", side); + parameters.Add("amount", quantity); + parameters.Add("price", price); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("postOnly", postOnly); + parameters.Add("ioc", immediateOrCancel); + parameters.Add("bboRole", bboRole); + parameters.Add("activation_price", triggerPrice); + parameters.Add("stopLoss", stopLossPrice); + parameters.Add("takeProfit", takeProfitPrice); + parameters.Add("stp", stpMode); + parameters.Add("positionSide", positionSide); + parameters.Add("reduceOnly", reduceOnly); string path; if (type == NewOrderType.Limit) @@ -85,10 +85,10 @@ public async Task> PlaceOrderAsync( #region Get Open Positions /// - public async Task> GetOpenPositionsAsync(string? symbol = null, CancellationToken ct = default) + public async Task> GetOpenPositionsAsync(string? symbol = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/positions/open", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -100,15 +100,15 @@ public async Task> GetOpenPositionsAsync(strin #region Get Position History /// - public async Task> GetPositionHistoryAsync(string? symbol = null, long? positionId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetPositionHistoryAsync(string? symbol = null, long? positionId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); - parameters.AddOptional("positionId", positionId); - parameters.AddOptionalMilliseconds("startDate", startTime); - parameters.AddOptionalMilliseconds("endDate", endTime); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); + parameters.Add("positionId", positionId); + parameters.Add("startDate", startTime); + parameters.Add("endDate", endTime); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/positions/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -120,12 +120,12 @@ public async Task> GetPositionHistoryAs #region Get Open Conditional Orders /// - public async Task> GetOpenConditionalOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetOpenConditionalOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/conditional-orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -137,7 +137,7 @@ public async Task> GetOpenConditi #region Place Oco Order /// - public async Task> PlaceOcoOrderAsync( + public async Task> PlaceOcoOrderAsync( string symbol, OrderSide orderSide, decimal quantity, @@ -149,16 +149,16 @@ public async Task> PlaceOcoOrderAsync( bool? reduceOnly = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.AddEnum("side", orderSide); - parameters.AddString("amount", quantity); - parameters.AddString("price", price); - parameters.AddString("activation_price", triggerPrice); - parameters.AddString("stop_limit_price", stopLimitPrice); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptionalEnum("positionSide", positionSide); - parameters.AddOptional("reduceOnly", reduceOnly); + parameters.Add("side", orderSide); + parameters.Add("amount", quantity); + parameters.Add("price", price); + parameters.Add("activation_price", triggerPrice); + parameters.Add("stop_limit_price", stopLimitPrice); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("positionSide", positionSide); + parameters.Add("reduceOnly", reduceOnly); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/collateral/oco", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -169,9 +169,9 @@ public async Task> PlaceOcoOrderAsync( #region Cancel Oco Order /// - public async Task> CancelOcoOrderAsync(string symbol, long orderId, CancellationToken ct = default) + public async Task> CancelOcoOrderAsync(string symbol, long orderId, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("orderId", orderId); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/oco-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, @@ -185,9 +185,9 @@ public async Task> CancelOcoOrderAsync(string sy #region Cancel Conditional Order /// - public async Task CancelConditionalOrderAsync(string symbol, long orderId, CancellationToken ct = default) + public async Task CancelConditionalOrderAsync(string symbol, long orderId, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("id", orderId); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/conditional-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, @@ -201,9 +201,9 @@ public async Task CancelConditionalOrderAsync(string symbol, long #region Cancel OTO Order /// - public async Task CancelOTOOrderAsync(string symbol, long orderId, CancellationToken ct = default) + public async Task CancelOTOOrderAsync(string symbol, long orderId, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("otoId", orderId); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/oto-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs index b70b26c..f196080 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs @@ -23,12 +23,12 @@ internal WhiteBitRestClientV4ApiConvert(WhiteBitRestClientV4Api baseClient) #region Get Convert Estimate /// - public async Task> GetConvertEstimateAsync(string fromAsset, string toAsset, decimal quantity, string fromOrToQuantity, CancellationToken ct = default) + public async Task> GetConvertEstimateAsync(string fromAsset, string toAsset, decimal quantity, string fromOrToQuantity, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("from", fromAsset); parameters.Add("to", toAsset); - parameters.AddString("amount", quantity); + parameters.Add("amount", quantity); parameters.Add("direction", fromOrToQuantity); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/estimate", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -41,9 +41,9 @@ public async Task> GetConvertEstimateAsyn #region Confirm Convert /// - public async Task> ConfirmConvertAsync(string estimateId, CancellationToken ct = default) + public async Task> ConfirmConvertAsync(string estimateId, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("quoteId", estimateId); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/confirm", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -56,16 +56,16 @@ public async Task> ConfirmConvertAsync(stri #region Get Convert History /// - public async Task> GetConvertHistoryAsync(string? fromAsset = null, string? toAsset = null, string? quoteId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetConvertHistoryAsync(string? fromAsset = null, string? toAsset = null, string? quoteId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("fromTicker", fromAsset); - parameters.AddOptional("toTicker", toAsset); - parameters.AddOptional("quoteId", quoteId); - parameters.AddOptionalMillisecondsString("from", startTime); - parameters.AddOptionalMillisecondsString("to", endTime); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("fromTicker", fromAsset); + parameters.Add("toTicker", toAsset); + parameters.Add("quoteId", quoteId); + parameters.Add("from", startTime); + parameters.Add("to", endTime); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs index b897f43..8681eb3 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs @@ -28,12 +28,12 @@ internal WhiteBitRestClientV4ApiExchangeData(ILogger logger, WhiteBitRestClientV #region Get Server Time /// - public async Task> GetServerTimeAsync(CancellationToken ct = default) + public async Task> GetServerTimeAsync(CancellationToken ct = default) { var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/time", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); - return result.As(result.Data?.Timestamp ?? default); + return HttpResult.Ok(result, result.Data?.Timestamp ?? default); } #endregion @@ -41,9 +41,9 @@ public async Task> GetServerTimeAsync(CancellationToken #region Get Symbols /// - public async Task> GetSymbolsAsync(CancellationToken ct = default) + public async Task> GetSymbolsAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -55,9 +55,9 @@ public async Task> GetSymbolsAsync(CancellationT #region Get System Status /// - public async Task> GetSystemStatusAsync(CancellationToken ct = default) + public async Task> GetSystemStatusAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/platform/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -69,19 +69,19 @@ public async Task> GetSystemStatusAsync(Canc #region Get Tickers /// - public async Task> GetTickersAsync(CancellationToken ct = default) + public async Task> GetTickersAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/ticker", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) item.Value.Symbol = item.Key; - return result.As(result.Data.Select(x => x.Value).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => x.Value).ToArray()); } #endregion @@ -89,19 +89,19 @@ public async Task> GetTickersAsync(CancellationT #region Get Assets /// - public async Task> GetAssetsAsync(CancellationToken ct = default) + public async Task> GetAssetsAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/assets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data?.Values.ToArray()); } #endregion @@ -109,11 +109,11 @@ public async Task> GetAssetsAsync(CancellationTok #region Get Order Book /// - public async Task> GetOrderBookAsync(string symbol, int? limit = null, int? mergeLevel = null, CancellationToken ct = default) + public async Task> GetOrderBookAsync(string symbol, int? limit = null, int? mergeLevel = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("limit", limit); - parameters.AddOptional("level", mergeLevel); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("limit", limit); + parameters.Add("level", mergeLevel); var request = _definitions.GetOrCreate(HttpMethod.Get, $"/api/v4/public/orderbook/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(600, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -125,10 +125,10 @@ public async Task> GetOrderBookAsync(string sym #region Get Recent Trades /// - public async Task> GetRecentTradesAsync(string symbol, OrderSide? side = null, CancellationToken ct = default) + public async Task> GetRecentTradesAsync(string symbol, OrderSide? side = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptionalEnum("type", side); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("type", side); var request = _definitions.GetOrCreate(HttpMethod.Get, $"/api/v4/public/trades/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -140,9 +140,9 @@ public async Task> GetRecentTradesAsync(string sy #region Get Deposit Withdrawal Info /// - public async Task> GetDepositWithdrawalInfoAsync(CancellationToken ct = default) + public async Task> GetDepositWithdrawalInfoAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -154,13 +154,13 @@ public async Task> GetDepositWithdrawalIn #region Get Colleteral Symbols /// - public async Task> GetCollateralSymbolsAsync(CancellationToken ct = default) + public async Task> GetCollateralSymbolsAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/collateral/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return result.As(result.Data?.Result); + return HttpResult.Ok(result, result.Data?.Result); } #endregion @@ -168,13 +168,13 @@ public async Task> GetCollateralSymbolsAsync(Cancellatio #region Get Futures Symbols /// - public async Task> GetFuturesSymbolsAsync(CancellationToken ct = default) + public async Task> GetFuturesSymbolsAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/futures", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return result.As(result.Data?.Result); + return HttpResult.Ok(result, result.Data?.Result); } #endregion @@ -182,7 +182,7 @@ public async Task> GetFuturesSymbolsAsync #region Get Funding History /// - public async Task> GetFundingHistoryAsync( + public async Task> GetFundingHistoryAsync( string symbol, DateTime? startTime = null, DateTime? endTime = null, @@ -190,11 +190,11 @@ public async Task> GetFundingHistoryAsyn int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptionalSeconds("startDate", startTime); - parameters.AddOptionalSeconds("endDate", endTime); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("startDate", startTime); + parameters.Add("endDate", endTime); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/funding-history/" + symbol, WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs index 6e12cb7..f159f8e 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs @@ -16,10 +16,11 @@ namespace WhiteBit.Net.Clients.V4Api { internal partial class WhiteBitRestClientV4Api : IWhiteBitRestClientV4ApiShared { + private const string _exchange = "WhiteBit"; private const string _topicSpotId = "WhiteBitSpot"; private const string _topicFuturesId = "WhiteBitFutures"; - public string Exchange => "WhiteBit"; + public string Exchange => _exchange; public TradingMode[] SupportedTradingModes => new[] { TradingMode.Spot, TradingMode.PerpetualLinear }; public TradingMode[] SupportedFuturesModes => new[] { TradingMode.PerpetualLinear }; @@ -28,22 +29,21 @@ internal partial class WhiteBitRestClientV4Api : IWhiteBitRestClientV4ApiShared public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); #region Spot Symbol client - EndpointOptions ISpotSymbolRestClient.GetSpotSymbolsOptions { get; } = new EndpointOptions(false); + EndpointOptions ISpotSymbolRestClient.GetSpotSymbolsOptions { get; } = new EndpointOptions(_exchange, false); - async Task> ISpotSymbolRestClient.GetSpotSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) + async Task> ISpotSymbolRestClient.GetSpotSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) { - var validationError = ((ISpotSymbolRestClient)this).GetSpotSymbolsOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetSpotSymbolsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await ExchangeData.GetSymbolsAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var data = result.Data.Where(x => x.SymbolType == Enums.SymbolType.Spot); - var response = result.AsExchangeResult(Exchange, TradingMode.Spot, - data.Select(s => new SharedSpotSymbol(s.BaseAsset, s.QuoteAsset, s.Name, s.TradingEnabled) + var response = HttpResult.Ok(result, data.Select(s => new SharedSpotSymbol(s.BaseAsset, s.QuoteAsset, s.Name, s.TradingEnabled) { MinTradeQuantity = s.MinOrderQuantity, MinNotionalValue = s.MinOrderValue, @@ -55,19 +55,19 @@ async Task> ISpotSymbolRestClient.GetSpotS return response; } - async Task> ISpotSymbolRestClient.GetSpotSymbolsForBaseAssetAsync(string baseAsset) + async Task> ISpotSymbolRestClient.GetSpotSymbolsForBaseAssetAsync(string baseAsset) { if (!ExchangeSymbolCache.HasCached(_topicSpotId)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicSpotId, baseAsset)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicSpotId, baseAsset)); } - async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(SharedSymbol symbol) + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(SharedSymbol symbol) { if (symbol.TradingMode != TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Only Spot symbols allowed"); @@ -75,62 +75,62 @@ async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(S if (!ExchangeSymbolCache.HasCached(_topicSpotId)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbol)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbol)); } - async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(string symbolName) + async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(string symbolName) { if (!ExchangeSymbolCache.HasCached(_topicSpotId)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbolName)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbolName)); } #endregion #region Ticker client - GetTickerOptions ISpotTickerRestClient.GetSpotTickerOptions { get; } = new GetTickerOptions(); - async Task> ISpotTickerRestClient.GetSpotTickerAsync(GetTickerRequest request, CancellationToken ct) + GetSpotTickerOptions ISpotTickerRestClient.GetSpotTickerOptions { get; } = new GetSpotTickerOptions(_exchange); + async Task> ISpotTickerRestClient.GetSpotTickerAsync(GetTickerRequest request, CancellationToken ct) { - var validationError = ((ISpotTickerRestClient)this).GetSpotTickerOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetSpotTickerOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await ExchangeData.GetTickersAsync(ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var ticker = result.Data.SingleOrDefault(x => x.Symbol == request.Symbol!.GetSymbol(FormatSymbol)); if (ticker == null) - return result.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); + return HttpResult.Fail(result, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); - return result.AsExchangeResult(Exchange, TradingMode.Spot, new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, null, null, ticker.BaseVolume, ticker.ChangePercentage) + return HttpResult.Ok(result, new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, null, null, ticker.BaseVolume, ticker.ChangePercentage) { QuoteVolume = ticker.QuoteVolume }); } - GetTickersOptions ISpotTickerRestClient.GetSpotTickersOptions { get; } = new GetTickersOptions(); - async Task> ISpotTickerRestClient.GetSpotTickersAsync(GetTickersRequest request, CancellationToken ct) + GetSpotTickersOptions ISpotTickerRestClient.GetSpotTickersOptions { get; } = new GetSpotTickersOptions(_exchange); + async Task> ISpotTickerRestClient.GetSpotTickersAsync(GetTickersRequest request, CancellationToken ct) { - var validationError = ((ISpotTickerRestClient)this).GetSpotTickersOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetSpotTickersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await ExchangeData.GetTickersAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var data = result.Data.Where(x => !x.Symbol.EndsWith("_PERP")); - return result.AsExchangeResult(Exchange, TradingMode.Spot, data.Select(x => new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), x.Symbol, x.LastPrice, null, null, x.BaseVolume, x.ChangePercentage) + return HttpResult.Ok(result, data.Select(x => new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), x.Symbol, x.LastPrice, null, null, x.BaseVolume, x.ChangePercentage) { QuoteVolume = x.QuoteVolume }).ToArray()); @@ -140,18 +140,18 @@ async Task> ISpotTickerRestClient.GetSpotT #region Book Ticker client - EndpointOptions IBookTickerRestClient.GetBookTickerOptions { get; } = new EndpointOptions(false); - async Task> IBookTickerRestClient.GetBookTickerAsync(GetBookTickerRequest request, CancellationToken ct) + EndpointOptions IBookTickerRestClient.GetBookTickerOptions { get; } = new EndpointOptions(_exchange, false); + async Task> IBookTickerRestClient.GetBookTickerAsync(GetBookTickerRequest request, CancellationToken ct) { - var validationError = ((IBookTickerRestClient)this).GetBookTickerOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetBookTickerOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var resultTicker = await ExchangeData.GetOrderBookAsync(request.Symbol!.GetSymbol(FormatSymbol), 1, ct: ct).ConfigureAwait(false); - if (!resultTicker) - return resultTicker.AsExchangeResult(Exchange, null, default); + if (!resultTicker.Success) + return HttpResult.Fail(resultTicker); - return resultTicker.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedBookTicker( + return HttpResult.Ok(resultTicker, new SharedBookTicker( ExchangeSymbolCache.ParseSymbol(request.Symbol.TradingMode == TradingMode.Spot ? _topicSpotId : _topicFuturesId, resultTicker.Data.Symbol), resultTicker.Data.Symbol, resultTicker.Data.Asks[0].Price, @@ -163,28 +163,28 @@ async Task> IBookTickerRestClient.GetBookTic #endregion #region Recent Trades client - GetRecentTradesOptions IRecentTradeRestClient.GetRecentTradesOptions { get; } = new GetRecentTradesOptions(100, false); + GetRecentTradesOptions IRecentTradeRestClient.GetRecentTradesOptions { get; } = new GetRecentTradesOptions(_exchange, 100, false); - async Task> IRecentTradeRestClient.GetRecentTradesAsync(GetRecentTradesRequest request, CancellationToken ct) + async Task> IRecentTradeRestClient.GetRecentTradesAsync(GetRecentTradesRequest request, CancellationToken ct) { - var validationError = ((IRecentTradeRestClient)this).GetRecentTradesOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetRecentTradesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); // Get data var symbol = request.Symbol!.GetSymbol(FormatSymbol); var result = await ExchangeData.GetRecentTradesAsync( symbol, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); IEnumerable data = result.Data; if (request.Limit != null) data = data.Take(request.Limit.Value); // Return - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, data.Select(x => + return HttpResult.Ok(result, data.Select(x => new SharedTrade(request.Symbol, symbol, x.BaseVolume, x.Price, x.Timestamp) { Side = x.Side == Enums.OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell @@ -193,76 +193,76 @@ async Task> IRecentTradeRestClient.GetRecentTra #endregion #region Order Book client - GetOrderBookOptions IOrderBookRestClient.GetOrderBookOptions { get; } = new GetOrderBookOptions(1, 100, false); - async Task> IOrderBookRestClient.GetOrderBookAsync(GetOrderBookRequest request, CancellationToken ct) + GetOrderBookOptions IOrderBookRestClient.GetOrderBookOptions { get; } = new GetOrderBookOptions(_exchange, 1, 100, false); + async Task> IOrderBookRestClient.GetOrderBookAsync(GetOrderBookRequest request, CancellationToken ct) { - var validationError = ((IOrderBookRestClient)this).GetOrderBookOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetOrderBookOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await ExchangeData.GetOrderBookAsync( request.Symbol!.GetSymbol(FormatSymbol), limit: request.Limit, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedOrderBook(result.Data.Asks, result.Data.Bids)); + return HttpResult.Ok(result, new SharedOrderBook(result.Data.Asks, result.Data.Bids)); } #endregion #region Balance Client - GetBalancesOptions IBalanceRestClient.GetBalancesOptions { get; } = new GetBalancesOptions(AccountTypeFilter.Funding, AccountTypeFilter.Spot, AccountTypeFilter.Futures); + GetBalancesOptions IBalanceRestClient.GetBalancesOptions { get; } = new GetBalancesOptions(_exchange, AccountTypeFilter.Funding, AccountTypeFilter.Spot, AccountTypeFilter.Futures); - async Task> IBalanceRestClient.GetBalancesAsync(GetBalancesRequest request, CancellationToken ct) + async Task> IBalanceRestClient.GetBalancesAsync(GetBalancesRequest request, CancellationToken ct) { - var validationError = ((IBalanceRestClient)this).GetBalancesOptions.ValidateRequest(Exchange, request, SupportedTradingModes); + var validationError = SharedClient.GetBalancesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (request.AccountType == null || request.AccountType == SharedAccountType.Spot) { var result = await Account.GetSpotBalancesAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, TradingMode.Spot, result.Data.Select(x => new SharedBalance(x.Asset, x.Available, x.Available + x.Frozen)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Asset, x.Available, x.Available + x.Frozen)).ToArray()); } else if(request.AccountType == SharedAccountType.Funding) { var result = await Account.GetMainBalancesAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, SupportedFuturesModes, result.Data.Select(x => new SharedBalance(x.Asset, x.MainBalance, x.MainBalance)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Asset, x.MainBalance, x.MainBalance)).ToArray()); } else { var result = await Account.GetCollateralBalancesAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, SupportedFuturesModes, result.Data.Select(x => new SharedBalance(x.Key, x.Value, x.Value)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Key, x.Value, x.Value)).ToArray()); } } #endregion #region Asset client - EndpointOptions IAssetsRestClient.GetAssetsOptions { get; } = new EndpointOptions(true); + EndpointOptions IAssetsRestClient.GetAssetsOptions { get; } = new EndpointOptions(_exchange, true); - async Task> IAssetsRestClient.GetAssetsAsync(GetAssetsRequest request, CancellationToken ct) + async Task> IAssetsRestClient.GetAssetsAsync(GetAssetsRequest request, CancellationToken ct) { - var validationError = ((IAssetsRestClient)this).GetAssetsOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetAssetsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var assets = await ExchangeData.GetAssetsAsync(ct: ct).ConfigureAwait(false); - if (!assets) - return assets.AsExchangeResult(Exchange, null, default); + if (!assets.Success) + return HttpResult.Fail(assets); - return assets.AsExchangeResult(Exchange, TradingMode.Spot, assets.Data.Select(x => + return HttpResult.Ok(assets, assets.Data.Select(x => { var networks = x.Networks.Withdraws.Intersect(x.Networks.Deposits); return new SharedAsset(x.Asset) @@ -277,23 +277,23 @@ async Task> IAssetsRestClient.GetAssetsAsync(Ge }).ToArray()); } - EndpointOptions IAssetsRestClient.GetAssetOptions { get; } = new EndpointOptions(false); - async Task> IAssetsRestClient.GetAssetAsync(GetAssetRequest request, CancellationToken ct) + EndpointOptions IAssetsRestClient.GetAssetOptions { get; } = new EndpointOptions(_exchange, false); + async Task> IAssetsRestClient.GetAssetAsync(GetAssetRequest request, CancellationToken ct) { - var validationError = ((IAssetsRestClient)this).GetAssetOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetAssetOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var assets = await ExchangeData.GetAssetsAsync(ct: ct).ConfigureAwait(false); - if (!assets) - return assets.AsExchangeResult(Exchange, null, default); + if (!assets.Success) + return HttpResult.Fail(assets); var asset = assets.Data.SingleOrDefault(x => x.Asset == request.Asset); if (asset == null) - return assets.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownAsset, "Asset not found"))); + return HttpResult.Fail(assets, new ServerError(new ErrorInfo(ErrorType.UnknownAsset, "Asset not found"))); var networks = asset.Networks.Withdraws.Intersect(asset.Networks.Deposits); - return assets.AsExchangeResult(Exchange, TradingMode.Spot, new SharedAsset(asset.Asset) + return HttpResult.Ok(assets, new SharedAsset(asset.Asset) { FullName = asset.Name, Networks = networks.Select(n => new SharedAssetNetwork(n) @@ -308,30 +308,30 @@ async Task> IAssetsRestClient.GetAssetAsync(GetAs #region Deposit client - EndpointOptions IDepositRestClient.GetDepositAddressesOptions { get; } = new EndpointOptions(true); - async Task> IDepositRestClient.GetDepositAddressesAsync(GetDepositAddressesRequest request, CancellationToken ct) + EndpointOptions IDepositRestClient.GetDepositAddressesOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IDepositRestClient.GetDepositAddressesAsync(GetDepositAddressesRequest request, CancellationToken ct) { - var validationError = ((IDepositRestClient)this).GetDepositAddressesOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetDepositAddressesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var depositAddresses = await Account.GetDepositAddressAsync(request.Asset, request.Network, ct: ct).ConfigureAwait(false); - if (!depositAddresses) - return depositAddresses.AsExchangeResult(Exchange, null, default); + if (!depositAddresses.Success) + return HttpResult.Fail(depositAddresses); - return depositAddresses.AsExchangeResult(Exchange, TradingMode.Spot, new[] { new SharedDepositAddress(request.Asset, depositAddresses.Data.Account.Address) + return HttpResult.Ok(depositAddresses, new[] { new SharedDepositAddress(request.Asset, depositAddresses.Data.Account.Address) { Network = request.Network } }); } - GetDepositsOptions IDepositRestClient.GetDepositsOptions { get; } = new GetDepositsOptions(false, true, false, 100); - async Task> IDepositRestClient.GetDepositsAsync(GetDepositsRequest request, PageRequest? pageRequest, CancellationToken ct) + GetDepositsOptions IDepositRestClient.GetDepositsOptions { get; } = new GetDepositsOptions(_exchange, false, true, false, 100); + async Task> IDepositRestClient.GetDepositsAsync(GetDepositsRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IDepositRestClient)this).GetDepositsOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetDepositsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -344,8 +344,8 @@ async Task> IDepositRestClient.GetDepositsAsy limit: limit, offset: pageParams.Offset, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -355,10 +355,7 @@ async Task> IDepositRestClient.GetDepositsAsy request.EndTime ?? DateTime.UtcNow, pageParams); - return result.AsExchangeResult( - Exchange, - TradingMode.Spot, - ExchangeHelpers.ApplyFilter(result.Data.Records, x => x.CreateTime, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data.Records, x => x.CreateTime, request.StartTime, request.EndTime, direction) .Select(x => new SharedDeposit( x.Asset, @@ -397,12 +394,12 @@ private SharedTransferStatus ParseTransferStatus(TransactionStatus? transactionS #region Withdrawal client - GetWithdrawalsOptions IWithdrawalRestClient.GetWithdrawalsOptions { get; } = new GetWithdrawalsOptions(false, true, false, 100); - async Task> IWithdrawalRestClient.GetWithdrawalsAsync(GetWithdrawalsRequest request, PageRequest? pageRequest, CancellationToken ct) + GetWithdrawalsOptions IWithdrawalRestClient.GetWithdrawalsOptions { get; } = new GetWithdrawalsOptions(_exchange, false, true, false, 100); + async Task> IWithdrawalRestClient.GetWithdrawalsAsync(GetWithdrawalsRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IWithdrawalRestClient)this).GetWithdrawalsOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.GetWithdrawalsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -415,8 +412,8 @@ async Task> IWithdrawalRestClient.GetWithd limit: limit, offset: pageParams.Offset, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -426,10 +423,7 @@ async Task> IWithdrawalRestClient.GetWithd request.EndTime ?? DateTime.UtcNow, pageParams); - return result.AsExchangeResult( - Exchange, - TradingMode.Spot, - ExchangeHelpers.ApplyFilter(result.Data.Records, x => x.CreateTime, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data.Records, x => x.CreateTime, request.StartTime, request.EndTime, direction) .Select(x => new SharedWithdrawal(x.Asset, x.Address, x.Quantity, x.TransactionStatus == Enums.TransactionStatus.Success, x.CreateTime) { @@ -447,12 +441,12 @@ async Task> IWithdrawalRestClient.GetWithd #region Withdraw client - WithdrawOptions IWithdrawRestClient.WithdrawOptions { get; } = new WithdrawOptions(); - async Task> IWithdrawRestClient.WithdrawAsync(WithdrawRequest request, CancellationToken ct) + WithdrawOptions IWithdrawRestClient.WithdrawOptions { get; } = new WithdrawOptions(_exchange); + async Task> IWithdrawRestClient.WithdrawAsync(WithdrawRequest request, CancellationToken ct) { - var validationError = ((IWithdrawRestClient)this).WithdrawOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.WithdrawOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var id = Guid.NewGuid().ToString(); // Get data @@ -465,10 +459,10 @@ async Task> IWithdrawRestClient.WithdrawAsync(Withdr network: request.Network, memo: request.AddressTag, ct: ct).ConfigureAwait(false); - if (!withdrawal) - return withdrawal.AsExchangeResult(Exchange, null, default); + if (!withdrawal.Success) + return HttpResult.Fail(withdrawal); - return withdrawal.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId(id)); + return HttpResult.Ok(withdrawal, new SharedId(id)); } #endregion @@ -487,19 +481,12 @@ async Task> IWithdrawRestClient.WithdrawAsync(Withdr string ISpotOrderRestClient.GenerateClientOrderId() => ExchangeHelpers.RandomString(32); - PlaceSpotOrderOptions ISpotOrderRestClient.PlaceSpotOrderOptions { get; } = new PlaceSpotOrderOptions(); - async Task> ISpotOrderRestClient.PlaceSpotOrderAsync(PlaceSpotOrderRequest request, CancellationToken ct) - { - var validationError = ((ISpotOrderRestClient)this).PlaceSpotOrderOptions.ValidateRequest( - Exchange, - request, - request.TradingMode, - [TradingMode.Spot], - ((ISpotOrderRestClient)this).SpotSupportedOrderTypes, - ((ISpotOrderRestClient)this).SpotSupportedTimeInForce, - ((ISpotOrderRestClient)this).SpotSupportedOrderQuantity); + PlaceSpotOrderOptions ISpotOrderRestClient.PlaceSpotOrderOptions { get; } = new PlaceSpotOrderOptions(_exchange); + async Task> ISpotOrderRestClient.PlaceSpotOrderAsync(PlaceSpotOrderRequest request, CancellationToken ct) + { + var validationError = SharedClient.PlaceSpotOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await Trading.PlaceSpotOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), @@ -513,30 +500,30 @@ async Task> ISpotOrderRestClient.PlaceSpotOrderAsync clientOrderId: request.ClientOrderId, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions ISpotOrderRestClient.GetSpotOrderOptions { get; } = new EndpointOptions(true); - async Task> ISpotOrderRestClient.GetSpotOrderAsync(GetOrderRequest request, CancellationToken ct) + EndpointOptions ISpotOrderRestClient.GetSpotOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ISpotOrderRestClient.GetSpotOrderAsync(GetOrderRequest request, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).GetSpotOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.GetSpotOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); var openOrders = await Trading.GetOpenOrdersAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!openOrders) - return openOrders.AsExchangeResult(Exchange, null, default); + if (!openOrders.Success) + return HttpResult.Fail(openOrders); var openOrder = openOrders.Data.SingleOrDefault(); if (openOrder != null) { - return openOrders.AsExchangeResult(Exchange, TradingMode.Spot, new SharedSpotOrder( + return HttpResult.Ok(openOrders, new SharedSpotOrder( ExchangeSymbolCache.ParseSymbol(_topicSpotId, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), @@ -560,16 +547,16 @@ async Task> ISpotOrderRestClient.GetSpotOrder else { var closeOrders = await Trading.GetClosedOrdersAsync(request.Symbol.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!closeOrders) - return closeOrders.AsExchangeResult(Exchange, null, default); + if (!closeOrders.Success) + return HttpResult.Fail(closeOrders); if (!closeOrders.Data.Any()) - return closeOrders.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); + return HttpResult.Fail(closeOrders, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); var closedOrder = closeOrders.Data.Single().Value.Single(); var status = closedOrder.Status == OrderStatus.Canceled ? SharedOrderStatus.Canceled : SharedOrderStatus.Filled; - return closeOrders.AsExchangeResult(Exchange, TradingMode.Spot, new SharedSpotOrder( + return HttpResult.Ok(closeOrders, new SharedSpotOrder( ExchangeSymbolCache.ParseSymbol(_topicSpotId, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), @@ -592,21 +579,21 @@ async Task> ISpotOrderRestClient.GetSpotOrder } } - EndpointOptions ISpotOrderRestClient.GetOpenSpotOrdersOptions { get; } = new EndpointOptions(true); - async Task> ISpotOrderRestClient.GetOpenSpotOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) + EndpointOptions ISpotOrderRestClient.GetOpenSpotOrdersOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ISpotOrderRestClient.GetOpenSpotOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).GetOpenSpotOrdersOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.GetOpenSpotOrdersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var symbol = request.Symbol?.GetSymbol(FormatSymbol); var orders = await Trading.GetOpenOrdersAsync(symbol, ct: ct).ConfigureAwait(false); - if (!orders) - return orders.AsExchangeResult(Exchange, null, default); + if (!orders.Success) + return HttpResult.Fail(orders); var data = orders.Data.Where(x => !x.Symbol.EndsWith("_PERP")); - return orders.AsExchangeResult(Exchange, TradingMode.Spot, data.Select(x => new SharedSpotOrder( + return HttpResult.Ok(orders, data.Select(x => new SharedSpotOrder( ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), x.Symbol, x.OrderId.ToString(), @@ -628,15 +615,15 @@ async Task> ISpotOrderRestClient.GetOpenSpo }).ToArray()); } - GetClosedOrdersOptions ISpotOrderRestClient.GetClosedSpotOrdersOptions { get; } = new GetClosedOrdersOptions(true, false, true, 100) + GetSpotClosedOrdersOptions ISpotOrderRestClient.GetClosedSpotOrdersOptions { get; } = new GetSpotClosedOrdersOptions(_exchange, true, false, true, 100) { MaxAge = TimeSpan.FromDays(180) }; - async Task> ISpotOrderRestClient.GetClosedSpotOrdersAsync(GetClosedOrdersRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> ISpotOrderRestClient.GetClosedSpotOrdersAsync(GetClosedOrdersRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).GetClosedSpotOrdersOptions.ValidateRequest(Exchange, request, request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.GetClosedSpotOrdersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -650,8 +637,8 @@ async Task> ISpotOrderRestClient.GetClosedS limit: limit, offset: pageParams.Offset, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -684,28 +671,24 @@ async Task> ISpotOrderRestClient.GetClosedS IsTriggerOrder = x.TriggerPrice > 0 })); - return result.AsExchangeResult( - Exchange, - TradingMode.Spot, - ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), - nextPageRequest); + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), nextPageRequest); } - EndpointOptions ISpotOrderRestClient.GetSpotOrderTradesOptions { get; } = new EndpointOptions(true); - async Task> ISpotOrderRestClient.GetSpotOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) + EndpointOptions ISpotOrderRestClient.GetSpotOrderTradesOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ISpotOrderRestClient.GetSpotOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).GetSpotOrderTradesOptions.ValidateRequest(Exchange, request, request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.GetSpotOrderTradesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderTradesRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderTradesRequest.OrderId), "Invalid order id")); var orders = await Trading.GetOrderTradesAsync(orderId, ct: ct).ConfigureAwait(false); - if (!orders) - return orders.AsExchangeResult(Exchange, null, default); + if (!orders.Success) + return HttpResult.Fail(orders); - return orders.AsExchangeResult(Exchange, TradingMode.Spot, orders.Data.Select(x => new SharedUserTrade( + return HttpResult.Ok(orders, orders.Data.Select(x => new SharedUserTrade( ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), x.Symbol, x.OrderId.ToString(), @@ -722,15 +705,15 @@ async Task> ISpotOrderRestClient.GetSpotOrd }).ToArray()); } - GetUserTradesOptions ISpotOrderRestClient.GetSpotUserTradesOptions { get; } = new GetUserTradesOptions(false, true, true, 100) + GetSpotUserTradesOptions ISpotOrderRestClient.GetSpotUserTradesOptions { get; } = new GetSpotUserTradesOptions(_exchange, false, true, true, 100) { MaxAge = TimeSpan.FromDays(180) }; - async Task> ISpotOrderRestClient.GetSpotUserTradesAsync(GetUserTradesRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> ISpotOrderRestClient.GetSpotUserTradesAsync(GetUserTradesRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).GetSpotUserTradesOptions.ValidateRequest(Exchange, request, request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.GetSpotUserTradesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -744,8 +727,8 @@ async Task> ISpotOrderRestClient.GetSpotUse offset: pageParams.Offset, ct: ct ).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -756,10 +739,7 @@ async Task> ISpotOrderRestClient.GetSpotUse pageParams, maxAge: TimeSpan.FromDays(180)); - return result.AsExchangeResult( - Exchange, - TradingMode.Spot, - ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) .Select(y => new SharedUserTrade( ExchangeSymbolCache.ParseSymbol(_topicSpotId, y.Symbol), y.Symbol, @@ -777,21 +757,21 @@ async Task> ISpotOrderRestClient.GetSpotUse }).ToArray(), nextPageRequest); } - EndpointOptions ISpotOrderRestClient.CancelSpotOrderOptions { get; } = new EndpointOptions(true); - async Task> ISpotOrderRestClient.CancelSpotOrderAsync(CancelOrderRequest request, CancellationToken ct) + EndpointOptions ISpotOrderRestClient.CancelSpotOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ISpotOrderRestClient.CancelSpotOrderAsync(CancelOrderRequest request, CancellationToken ct) { - var validationError = ((ISpotOrderRestClient)this).CancelSpotOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, [TradingMode.Spot]); + var validationError = SharedClient.CancelSpotOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); var order = await Trading.CancelOrderAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!order) - return order.AsExchangeResult(Exchange, null, default); + if (!order.Success) + return HttpResult.Fail(order); - return order.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId(order.Data.OrderId.ToString())); + return HttpResult.Ok(order, new SharedId(order.Data.OrderId.ToString())); } private SharedOrderType ParseOrderType(OrderType type, bool postOnly) @@ -816,25 +796,27 @@ private SharedOrderType ParseOrderType(OrderType type, bool postOnly) #region Futures Symbol client - EndpointOptions IFuturesSymbolRestClient.GetFuturesSymbolsOptions { get; } = new EndpointOptions(false); - async Task> IFuturesSymbolRestClient.GetFuturesSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) + EndpointOptions IFuturesSymbolRestClient.GetFuturesSymbolsOptions { get; } = new EndpointOptions(_exchange, false); + async Task> IFuturesSymbolRestClient.GetFuturesSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) { - var validationError = ((IFuturesSymbolRestClient)this).GetFuturesSymbolsOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesSymbolsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); - - var symbols = ExchangeData.GetSymbolsAsync(ct); - var futuresSymbols = ExchangeData.GetFuturesSymbolsAsync(ct); - await Task.WhenAll(symbols, futuresSymbols).ConfigureAwait(false); - if (!symbols.Result) - return symbols.Result.AsExchangeResult(Exchange, null, default); - if (!futuresSymbols.Result) - return futuresSymbols.Result.AsExchangeResult(Exchange, null, default); - - var response = futuresSymbols.Result.AsExchangeResult(Exchange, request.TradingMode == null ? SupportedTradingModes : new[] { request.TradingMode.Value }, - futuresSymbols.Result.Data.Select(s => + return HttpResult.Fail(Exchange, validationError); + + var symbolsTask = ExchangeData.GetSymbolsAsync(ct); + var futuresSymbolsTask = ExchangeData.GetFuturesSymbolsAsync(ct); + await Task.WhenAll(symbolsTask, futuresSymbolsTask).ConfigureAwait(false); + + var symbols = symbolsTask.Result; + var futuresSymbols = futuresSymbolsTask.Result; + if (!symbols.Success) + return HttpResult.Fail(symbols); + if (!futuresSymbols.Success) + return HttpResult.Fail(futuresSymbols); + + var response = HttpResult.Ok(futuresSymbols, futuresSymbols.Data.Select(s => { - var symbol = symbols.Result.Data.SingleOrDefault(x => x.Name == s.Symbol); + var symbol = symbols.Data.SingleOrDefault(x => x.Name == s.Symbol); return new SharedFuturesSymbol(s.ProductType == ProductType.Perpetual ? TradingMode.PerpetualLinear : TradingMode.DeliveryLinear, s.BaseAsset, s.QuoteAsset, s.Symbol, true) { MinTradeQuantity = symbol?.MinOrderQuantity, @@ -849,19 +831,19 @@ async Task> IFuturesSymbolRestClient.Ge return response; } - async Task> IFuturesSymbolRestClient.GetFuturesSymbolsForBaseAssetAsync(string baseAsset) + async Task> IFuturesSymbolRestClient.GetFuturesSymbolsForBaseAssetAsync(string baseAsset) { if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicFuturesId, baseAsset)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicFuturesId, baseAsset)); } - async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(SharedSymbol symbol) + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(SharedSymbol symbol) { if (symbol.TradingMode == TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Spot symbols not allowed"); @@ -869,46 +851,46 @@ async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolA if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbol)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbol)); } - async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(string symbolName) + async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(string symbolName) { if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); - if (!symbols) - return new ExchangeResult(Exchange, symbols.Error!); + if (!symbols.Success) + return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return new ExchangeResult(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbolName)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbolName)); } #endregion #region Futures Ticker client - GetTickerOptions IFuturesTickerRestClient.GetFuturesTickerOptions { get; } = new GetTickerOptions(); - async Task> IFuturesTickerRestClient.GetFuturesTickerAsync(GetTickerRequest request, CancellationToken ct) + GetFuturesTickerOptions IFuturesTickerRestClient.GetFuturesTickerOptions { get; } = new GetFuturesTickerOptions(_exchange); + async Task> IFuturesTickerRestClient.GetFuturesTickerAsync(GetTickerRequest request, CancellationToken ct) { - var validationError = ((IFuturesTickerRestClient)this).GetFuturesTickerOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesTickerOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var resultTicker = await ExchangeData.GetFuturesSymbolsAsync(ct).ConfigureAwait(false); - if (!resultTicker) - return resultTicker.AsExchangeResult(Exchange, null, default); + if (!resultTicker.Success) + return HttpResult.Fail(resultTicker); var symbol = request.Symbol!.GetSymbol(FormatSymbol); var ticker = resultTicker.Data.SingleOrDefault(x => x.Symbol == symbol); if (ticker == null) - return resultTicker.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); + return HttpResult.Fail(resultTicker, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); - return resultTicker.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, ticker.HighPrice, ticker.LowPrice, ticker.BaseVolume, null) + return HttpResult.Ok(resultTicker, new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, ticker.HighPrice, ticker.LowPrice, ticker.BaseVolume, null) { IndexPrice = ticker.IndexPrice, FundingRate = ticker.FundingRate, @@ -916,18 +898,18 @@ async Task> IFuturesTickerRestClient.GetF }); } - GetTickersOptions IFuturesTickerRestClient.GetFuturesTickersOptions { get; } = new GetTickersOptions(); - async Task> IFuturesTickerRestClient.GetFuturesTickersAsync(GetTickersRequest request, CancellationToken ct) + GetFuturesTickersOptions IFuturesTickerRestClient.GetFuturesTickersOptions { get; } = new GetFuturesTickersOptions(_exchange); + async Task> IFuturesTickerRestClient.GetFuturesTickersAsync(GetTickersRequest request, CancellationToken ct) { - var validationError = ((IFuturesTickerRestClient)this).GetFuturesTickersOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesTickersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var resultTickers = await ExchangeData.GetFuturesSymbolsAsync(ct).ConfigureAwait(false); - if (!resultTickers) - return resultTickers.AsExchangeResult(Exchange, null, default); + if (!resultTickers.Success) + return HttpResult.Fail(resultTickers); - return resultTickers.AsExchangeResult(Exchange, request.TradingMode == null ? SupportedTradingModes : new[] { request.TradingMode.Value }, resultTickers.Data.Select(x => + return HttpResult.Ok(resultTickers, resultTickers.Data.Select(x => { return new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, x.LastPrice, x.HighPrice, x.LowPrice, x.BaseVolume, null) { @@ -943,72 +925,72 @@ async Task> IFuturesTickerRestClient.Ge #region Leverage client SharedLeverageSettingMode ILeverageRestClient.LeverageSettingType => SharedLeverageSettingMode.PerAccount; - EndpointOptions ILeverageRestClient.GetLeverageOptions { get; } = new EndpointOptions(true); - async Task> ILeverageRestClient.GetLeverageAsync(GetLeverageRequest request, CancellationToken ct) + EndpointOptions ILeverageRestClient.GetLeverageOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ILeverageRestClient.GetLeverageAsync(GetLeverageRequest request, CancellationToken ct) { - var validationError = ((ILeverageRestClient)this).GetLeverageOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetLeverageOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await Account.GetCollateralAccountSummaryAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, request.Symbol!.TradingMode, new SharedLeverage(result.Data.Leverage)); + return HttpResult.Ok(result, new SharedLeverage(result.Data.Leverage)); } - SetLeverageOptions ILeverageRestClient.SetLeverageOptions { get; } = new SetLeverageOptions(); - async Task> ILeverageRestClient.SetLeverageAsync(SetLeverageRequest request, CancellationToken ct) + SetLeverageOptions ILeverageRestClient.SetLeverageOptions { get; } = new SetLeverageOptions(_exchange); + async Task> ILeverageRestClient.SetLeverageAsync(SetLeverageRequest request, CancellationToken ct) { - var validationError = ((ILeverageRestClient)this).SetLeverageOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.SetLeverageOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await Account.SetAccountLeverageAsync((int)request.Leverage, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, request.TradingMode, new SharedLeverage(result.Data.Leverage)); + return HttpResult.Ok(result, new SharedLeverage(result.Data.Leverage)); } #endregion #region Open Interest client - EndpointOptions IOpenInterestRestClient.GetOpenInterestOptions { get; } = new EndpointOptions(true); - async Task> IOpenInterestRestClient.GetOpenInterestAsync(GetOpenInterestRequest request, CancellationToken ct) + EndpointOptions IOpenInterestRestClient.GetOpenInterestOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IOpenInterestRestClient.GetOpenInterestAsync(GetOpenInterestRequest request, CancellationToken ct) { - var validationError = ((IOpenInterestRestClient)this).GetOpenInterestOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetOpenInterestOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var resultTicker = await ExchangeData.GetFuturesSymbolsAsync(ct).ConfigureAwait(false); - if (!resultTicker) - return resultTicker.AsExchangeResult(Exchange, null, default); + if (!resultTicker.Success) + return HttpResult.Fail(resultTicker); var symbol = request.Symbol!.GetSymbol(FormatSymbol); var ticker = resultTicker.Data.SingleOrDefault(x => x.Symbol == symbol); if (ticker == null) - return resultTicker.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); + return HttpResult.Fail(resultTicker, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); - return resultTicker.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedOpenInterest(ticker.OpenInterest)); + return HttpResult.Ok(resultTicker, new SharedOpenInterest(ticker.OpenInterest)); } #endregion #region Position History client - GetPositionHistoryOptions IPositionHistoryRestClient.GetPositionHistoryOptions { get; } = new GetPositionHistoryOptions(false, true, true, 100) + GetPositionHistoryOptions IPositionHistoryRestClient.GetPositionHistoryOptions { get; } = new GetPositionHistoryOptions(_exchange, false, true, true, 100) { RequiredOptionalParameters = new List { new ParameterDescription(nameof(GetPositionHistoryRequest.Symbol), typeof(SharedSymbol), "The symbol to get position history for", "ETH_PERP") } }; - async Task> IPositionHistoryRestClient.GetPositionHistoryAsync(GetPositionHistoryRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> IPositionHistoryRestClient.GetPositionHistoryAsync(GetPositionHistoryRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IPositionHistoryRestClient)this).GetPositionHistoryOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetPositionHistoryOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -1023,8 +1005,8 @@ async Task> IPositionHistoryRestClien offset: pageParams.Offset, ct: ct ).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -1034,10 +1016,7 @@ async Task> IPositionHistoryRestClien request.EndTime ?? DateTime.UtcNow, pageParams); - return result.AsExchangeResult( - Exchange, - request.Symbol.TradingMode, - ExchangeHelpers.ApplyFilter(result.Data, x => x.OpenTime, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.OpenTime, request.StartTime, request.EndTime, direction) .Select(x => new SharedPositionHistory( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), @@ -1069,19 +1048,12 @@ async Task> IPositionHistoryRestClien string IFuturesOrderRestClient.GenerateClientOrderId() => ExchangeHelpers.RandomString(32); - PlaceFuturesOrderOptions IFuturesOrderRestClient.PlaceFuturesOrderOptions { get; } = new PlaceFuturesOrderOptions(true); - async Task> IFuturesOrderRestClient.PlaceFuturesOrderAsync(PlaceFuturesOrderRequest request, CancellationToken ct) - { - var validationError = ((IFuturesOrderRestClient)this).PlaceFuturesOrderOptions.ValidateRequest( - Exchange, - request, - request.TradingMode, - SupportedFuturesModes, - ((IFuturesOrderRestClient)this).FuturesSupportedOrderTypes, - ((IFuturesOrderRestClient)this).FuturesSupportedTimeInForce, - ((IFuturesOrderRestClient)this).FuturesSupportedOrderQuantity); + PlaceFuturesOrderOptions IFuturesOrderRestClient.PlaceFuturesOrderOptions { get; } = new PlaceFuturesOrderOptions(_exchange, true); + async Task> IFuturesOrderRestClient.PlaceFuturesOrderAsync(PlaceFuturesOrderRequest request, CancellationToken ct) + { + var validationError = SharedClient.PlaceFuturesOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await CollateralTrading.PlaceOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), @@ -1098,30 +1070,30 @@ async Task> IFuturesOrderRestClient.PlaceFuturesOrde reduceOnly: request.ReduceOnly, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions IFuturesOrderRestClient.GetFuturesOrderOptions { get; } = new EndpointOptions(true); - async Task> IFuturesOrderRestClient.GetFuturesOrderAsync(GetOrderRequest request, CancellationToken ct) + EndpointOptions IFuturesOrderRestClient.GetFuturesOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesOrderRestClient.GetFuturesOrderAsync(GetOrderRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetFuturesOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); var openOrders = await Trading.GetOpenOrdersAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!openOrders) - return openOrders.AsExchangeResult(Exchange, null, default); + if (!openOrders.Success) + return HttpResult.Fail(openOrders); var openOrder = openOrders.Data.SingleOrDefault(); if (openOrder != null) { - return openOrders.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesOrder( + return HttpResult.Ok(openOrders, new SharedFuturesOrder( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), @@ -1148,18 +1120,18 @@ async Task> IFuturesOrderRestClient.GetFut else { var closeOrders = await Trading.GetClosedOrdersAsync(request.Symbol.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!closeOrders) - return closeOrders.AsExchangeResult(Exchange, null, default); + if (!closeOrders.Success) + return HttpResult.Fail(closeOrders); if (!closeOrders.Data.Any()) - return closeOrders.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); + return HttpResult.Fail(closeOrders, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); var closedOrder = closeOrders.Data.Single().Value.Single(); var status = closedOrder.Status is OrderStatus.Canceled or OrderStatus.AutoCanceledUserMargin ? SharedOrderStatus.Canceled : SharedOrderStatus.Filled; - return closeOrders.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesOrder( + return HttpResult.Ok(closeOrders, new SharedFuturesOrder( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), @@ -1185,21 +1157,21 @@ async Task> IFuturesOrderRestClient.GetFut } } - EndpointOptions IFuturesOrderRestClient.GetOpenFuturesOrdersOptions { get; } = new EndpointOptions(true); - async Task> IFuturesOrderRestClient.GetOpenFuturesOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) + EndpointOptions IFuturesOrderRestClient.GetOpenFuturesOrdersOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesOrderRestClient.GetOpenFuturesOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetOpenFuturesOrdersOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetOpenFuturesOrdersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var symbol = request.Symbol?.GetSymbol(FormatSymbol); var orders = await Trading.GetOpenOrdersAsync(symbol, ct: ct).ConfigureAwait(false); - if (!orders) - return orders.AsExchangeResult(Exchange, null, default); + if (!orders.Success) + return HttpResult.Fail(orders); var data = orders.Data.Where(x => x.Symbol.EndsWith("_PERP")); - return orders.AsExchangeResult(Exchange, SupportedFuturesModes, [.. data.Select(x => new SharedFuturesOrder( + return HttpResult.Ok(orders, [.. data.Select(x => new SharedFuturesOrder( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, x.OrderId.ToString(), @@ -1224,15 +1196,15 @@ async Task> IFuturesOrderRestClient.GetO })]); } - GetClosedOrdersOptions IFuturesOrderRestClient.GetClosedFuturesOrdersOptions { get; } = new GetClosedOrdersOptions(false, true, true, 100) + GetFuturesClosedOrdersOptions IFuturesOrderRestClient.GetClosedFuturesOrdersOptions { get; } = new GetFuturesClosedOrdersOptions(_exchange, false, true, true, 100) { MaxAge = TimeSpan.FromDays(180) }; - async Task> IFuturesOrderRestClient.GetClosedFuturesOrdersAsync(GetClosedOrdersRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> IFuturesOrderRestClient.GetClosedFuturesOrdersAsync(GetClosedOrdersRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetClosedFuturesOrdersOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetClosedFuturesOrdersOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -1246,8 +1218,8 @@ async Task> IFuturesOrderRestClient.GetC limit: limit, offset: pageParams.Offset, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -1285,28 +1257,24 @@ x.Status is OrderStatus.Canceled or OrderStatus.AutoCanceledUserMargin UpdateTime = x.FillTime ?? x.UpdateTime })); - return result.AsExchangeResult( - Exchange, - request.Symbol.TradingMode, - ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), - nextPageRequest); + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), nextPageRequest); } - EndpointOptions IFuturesOrderRestClient.GetFuturesOrderTradesOptions { get; } = new EndpointOptions(true); - async Task> IFuturesOrderRestClient.GetFuturesOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) + EndpointOptions IFuturesOrderRestClient.GetFuturesOrderTradesOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesOrderRestClient.GetFuturesOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetFuturesOrderTradesOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesOrderTradesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderTradesRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderTradesRequest.OrderId), "Invalid order id")); var orders = await Trading.GetOrderTradesAsync(orderId, ct: ct).ConfigureAwait(false); - if (!orders) - return orders.AsExchangeResult(Exchange, null, default); + if (!orders.Success) + return HttpResult.Fail(orders); - return orders.AsExchangeResult(Exchange, request.TradingMode, orders.Data.Select(x => new SharedUserTrade( + return HttpResult.Ok(orders, orders.Data.Select(x => new SharedUserTrade( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), request.Symbol!.GetSymbol(FormatSymbol), x.OrderId.ToString(), @@ -1323,15 +1291,15 @@ async Task> IFuturesOrderRestClient.GetFutu }).ToArray()); } - GetUserTradesOptions IFuturesOrderRestClient.GetFuturesUserTradesOptions { get; } = new GetUserTradesOptions(false, true, true, 100) + GetFuturesUserTradesOptions IFuturesOrderRestClient.GetFuturesUserTradesOptions { get; } = new GetFuturesUserTradesOptions(_exchange, false, true, true, 100) { MaxAge = TimeSpan.FromDays(180) }; - async Task> IFuturesOrderRestClient.GetFuturesUserTradesAsync(GetUserTradesRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> IFuturesOrderRestClient.GetFuturesUserTradesAsync(GetUserTradesRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetFuturesUserTradesOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetFuturesUserTradesOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -1345,8 +1313,8 @@ async Task> IFuturesOrderRestClient.GetFutu offset: pageParams.Offset, ct: ct ).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -1357,10 +1325,7 @@ async Task> IFuturesOrderRestClient.GetFutu pageParams, maxAge: TimeSpan.FromDays(180)); - return result.AsExchangeResult( - Exchange, - TradingMode.Spot, - ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) .Select(y => new SharedUserTrade( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, y.Symbol), @@ -1380,37 +1345,37 @@ async Task> IFuturesOrderRestClient.GetFutu .ToArray(), nextPageRequest); } - EndpointOptions IFuturesOrderRestClient.CancelFuturesOrderOptions { get; } = new EndpointOptions(true); - async Task> IFuturesOrderRestClient.CancelFuturesOrderAsync(CancelOrderRequest request, CancellationToken ct) + EndpointOptions IFuturesOrderRestClient.CancelFuturesOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesOrderRestClient.CancelFuturesOrderAsync(CancelOrderRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).CancelFuturesOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.CancelFuturesOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); var order = await Trading.CancelOrderAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!order) - return order.AsExchangeResult(Exchange, null, default); + if (!order.Success) + return HttpResult.Fail(order); - return order.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(order.Data.OrderId.ToString())); + return HttpResult.Ok(order, new SharedId(order.Data.OrderId.ToString())); } - EndpointOptions IFuturesOrderRestClient.GetPositionsOptions { get; } = new EndpointOptions(true); - async Task> IFuturesOrderRestClient.GetPositionsAsync(GetPositionsRequest request, CancellationToken ct) + EndpointOptions IFuturesOrderRestClient.GetPositionsOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesOrderRestClient.GetPositionsAsync(GetPositionsRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).GetPositionsOptions.ValidateRequest(Exchange, request, request.Symbol?.TradingMode ?? request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.GetPositionsOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await CollateralTrading.GetOpenPositionsAsync(symbol: request.Symbol?.GetSymbol(FormatSymbol), ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var data = result.Data; var resultTypes = request.Symbol == null && request.TradingMode == null ? SupportedTradingModes : request.Symbol != null ? new[] { request.Symbol.TradingMode } : new[] { request.TradingMode!.Value }; - return result.AsExchangeResult(Exchange, resultTypes, data.Select(x => + return HttpResult.Ok(result, data.Select(x => new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, Math.Abs(x.Quantity), x.UpdateTime) { UnrealizedPnl = x.Pnl, @@ -1423,7 +1388,7 @@ async Task> IFuturesOrderRestClient.GetPosit }).ToArray()); } - EndpointOptions IFuturesOrderRestClient.ClosePositionOptions { get; } = new EndpointOptions(true) + EndpointOptions IFuturesOrderRestClient.ClosePositionOptions { get; } = new EndpointOptions(_exchange, true) { RequiredOptionalParameters = new List { @@ -1431,11 +1396,11 @@ async Task> IFuturesOrderRestClient.GetPosit new ParameterDescription(nameof(ClosePositionRequest.Quantity), typeof(decimal), "Quantity of the position is required", 0.1m), } }; - async Task> IFuturesOrderRestClient.ClosePositionAsync(ClosePositionRequest request, CancellationToken ct) + async Task> IFuturesOrderRestClient.ClosePositionAsync(ClosePositionRequest request, CancellationToken ct) { - var validationError = ((IFuturesOrderRestClient)this).ClosePositionOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.ClosePositionOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var symbol = request.Symbol!.GetSymbol(FormatSymbol); var result = await CollateralTrading.PlaceOrderAsync( @@ -1444,48 +1409,48 @@ async Task> IFuturesOrderRestClient.ClosePositionAsy NewOrderType.Market, request.Quantity, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } #endregion #region Fee Client - EndpointOptions IFeeRestClient.GetFeeOptions { get; } = new EndpointOptions(false); + EndpointOptions IFeeRestClient.GetFeeOptions { get; } = new EndpointOptions(_exchange, false); - async Task> IFeeRestClient.GetFeesAsync(GetFeeRequest request, CancellationToken ct) + async Task> IFeeRestClient.GetFeesAsync(GetFeeRequest request, CancellationToken ct) { - var validationError = ((IFeeRestClient)this).GetFeeOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetFeeOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); // Get data var result = await ExchangeData.GetSymbolsAsync(ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var symbol = result.Data.SingleOrDefault(x => x.Name == request.Symbol!.GetSymbol(FormatSymbol)); if (symbol == null) - return result.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); + return HttpResult.Fail(result, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); // Return - return result.AsExchangeResult(Exchange, request.TradingMode, new SharedFee(symbol.MakerFee, symbol.TakerFee)); + return HttpResult.Ok(result, new SharedFee(symbol.MakerFee, symbol.TakerFee)); } #endregion #region Spot Trigger Order Client - PlaceSpotTriggerOrderOptions ISpotTriggerOrderRestClient.PlaceSpotTriggerOrderOptions { get; } = new PlaceSpotTriggerOrderOptions(true) + PlaceSpotTriggerOrderOptions ISpotTriggerOrderRestClient.PlaceSpotTriggerOrderOptions { get; } = new PlaceSpotTriggerOrderOptions(_exchange, true) { }; - async Task> ISpotTriggerOrderRestClient.PlaceSpotTriggerOrderAsync(PlaceSpotTriggerOrderRequest request, CancellationToken ct) + async Task> ISpotTriggerOrderRestClient.PlaceSpotTriggerOrderAsync(PlaceSpotTriggerOrderRequest request, CancellationToken ct) { - var validationError = ((ISpotTriggerOrderRestClient)this).PlaceSpotTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes, ((ISpotOrderRestClient)this).SpotSupportedOrderQuantity); + var validationError = SharedClient.PlaceSpotTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await Trading.PlaceSpotOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), @@ -1497,33 +1462,33 @@ async Task> ISpotTriggerOrderRestClient.PlaceSpotTri price: request.OrderPrice, clientOrderId: request.ClientOrderId, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); // Return - return result.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions ISpotTriggerOrderRestClient.GetSpotTriggerOrderOptions { get; } = new EndpointOptions(true) + EndpointOptions ISpotTriggerOrderRestClient.GetSpotTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true) { }; - async Task> ISpotTriggerOrderRestClient.GetSpotTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) + async Task> ISpotTriggerOrderRestClient.GetSpotTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) { - var validationError = ((ISpotTriggerOrderRestClient)this).GetSpotTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetSpotTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); var openOrders = await Trading.GetOpenOrdersAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!openOrders) - return openOrders.AsExchangeResult(Exchange, null, default); + if (!openOrders.Success) + return HttpResult.Fail(openOrders); var openOrder = openOrders.Data.SingleOrDefault(); if (openOrder != null) { - return openOrders.AsExchangeResult(Exchange, TradingMode.Spot, new SharedSpotTriggerOrder( + return HttpResult.Ok(openOrders, new SharedSpotTriggerOrder( ExchangeSymbolCache.ParseSymbol(_topicSpotId, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), @@ -1547,14 +1512,14 @@ async Task> ISpotTriggerOrderRestClien else { var closeOrders = await Trading.GetClosedOrdersAsync(request.Symbol.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!closeOrders) - return closeOrders.AsExchangeResult(Exchange, null, default); + if (!closeOrders.Success) + return HttpResult.Fail(closeOrders); if (!closeOrders.Data.Any()) - return closeOrders.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); + return HttpResult.Fail(closeOrders, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); var closedOrder = closeOrders.Data.Single().Value.Single(); - return closeOrders.AsExchangeResult(Exchange, TradingMode.Spot, new SharedSpotTriggerOrder( + return HttpResult.Ok(closeOrders, new SharedSpotTriggerOrder( ExchangeSymbolCache.ParseSymbol(_topicSpotId, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), @@ -1577,39 +1542,39 @@ async Task> ISpotTriggerOrderRestClien } } - EndpointOptions ISpotTriggerOrderRestClient.CancelSpotTriggerOrderOptions { get; } = new EndpointOptions(true); - async Task> ISpotTriggerOrderRestClient.CancelSpotTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) + EndpointOptions ISpotTriggerOrderRestClient.CancelSpotTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> ISpotTriggerOrderRestClient.CancelSpotTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) { - var validationError = ((ISpotTriggerOrderRestClient)this).CancelSpotTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.CancelSpotTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); var order = await Trading.CancelOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!order) - return order.AsExchangeResult(Exchange, null, default); + if (!order.Success) + return HttpResult.Fail(order); - return order.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId(request.OrderId)); + return HttpResult.Ok(order, new SharedId(request.OrderId)); } #endregion #region Futures Trigger Order Client - PlaceFuturesTriggerOrderOptions IFuturesTriggerOrderRestClient.PlaceFuturesTriggerOrderOptions { get; } = new PlaceFuturesTriggerOrderOptions(false) + PlaceFuturesTriggerOrderOptions IFuturesTriggerOrderRestClient.PlaceFuturesTriggerOrderOptions { get; } = new PlaceFuturesTriggerOrderOptions(_exchange, false) { }; - async Task> IFuturesTriggerOrderRestClient.PlaceFuturesTriggerOrderAsync(PlaceFuturesTriggerOrderRequest request, CancellationToken ct) + async Task> IFuturesTriggerOrderRestClient.PlaceFuturesTriggerOrderAsync(PlaceFuturesTriggerOrderRequest request, CancellationToken ct) { var side = GetOrderSide(request); - var validationError = ((IFuturesTriggerOrderRestClient)this).PlaceFuturesTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes, side == OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell, ((IFuturesOrderRestClient)this).FuturesSupportedOrderQuantity); + var validationError = SharedClient.PlaceFuturesTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await CollateralTrading.PlaceOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), @@ -1621,11 +1586,11 @@ async Task> IFuturesTriggerOrderRestClient.PlaceFutu clientOrderId: request.ClientOrderId, positionSide: request.PositionSide.ToPositionSide(), ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); // Return - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } private OrderSide GetOrderSide(PlaceFuturesTriggerOrderRequest request) @@ -1636,26 +1601,26 @@ private OrderSide GetOrderSide(PlaceFuturesTriggerOrderRequest request) return request.OrderDirection == SharedTriggerOrderDirection.Enter ? OrderSide.Sell : OrderSide.Buy; } - EndpointOptions IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderOptions { get; } = new EndpointOptions(true) + EndpointOptions IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true) { }; - async Task> IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) + async Task> IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) { - var validationError = ((IFuturesTriggerOrderRestClient)this).GetFuturesTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetFuturesTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(GetOrderRequest.OrderId), "Invalid order id")); var openOrders = await Trading.GetOpenOrdersAsync(request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!openOrders) - return openOrders.AsExchangeResult(Exchange, null, default); + if (!openOrders.Success) + return HttpResult.Fail(openOrders); var openOrder = openOrders.Data.SingleOrDefault(); if (openOrder != null) { - return openOrders.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesTriggerOrder( + return HttpResult.Ok(openOrders, new SharedFuturesTriggerOrder( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), @@ -1680,15 +1645,15 @@ async Task> IFuturesTriggerOrderRes else { var closeOrders = await Trading.GetClosedOrdersAsync(request.Symbol.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!closeOrders) - return closeOrders.AsExchangeResult(Exchange, null, default); + if (!closeOrders.Success) + return HttpResult.Fail(closeOrders); if (!closeOrders.Data.Any()) - return closeOrders.AsExchangeError(Exchange, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); + return HttpResult.Fail(closeOrders, new ServerError(new ErrorInfo(ErrorType.UnknownOrder, "Order not found"))); var closedOrder = closeOrders.Data.Single().Value.Single(); - return closeOrders.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedFuturesTriggerOrder( + return HttpResult.Ok(closeOrders, new SharedFuturesTriggerOrder( ExchangeSymbolCache.ParseSymbol(_topicFuturesId, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), @@ -1725,30 +1690,30 @@ private SharedTriggerOrderStatus ParseTriggerOrderStatus(WhiteBitClosedOrder clo return SharedTriggerOrderStatus.Unknown; } - EndpointOptions IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderOptions { get; } = new EndpointOptions(true); - async Task> IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) + EndpointOptions IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true); + async Task> IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) { - var validationError = ((IFuturesTriggerOrderRestClient)this).CancelFuturesTriggerOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.CancelFuturesTriggerOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(CancelOrderRequest.OrderId), "Invalid order id")); var order = await Trading.CancelOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!order) - return order.AsExchangeResult(Exchange, null, default); + if (!order.Success) + return HttpResult.Fail(order); - return order.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(request.OrderId)); + return HttpResult.Ok(order, new SharedId(request.OrderId)); } #endregion #region Tp/SL Client - EndpointOptions IFuturesTpSlRestClient.SetFuturesTpSlOptions { get; } = new EndpointOptions(true) + EndpointOptions IFuturesTpSlRestClient.SetFuturesTpSlOptions { get; } = new EndpointOptions(_exchange, true) { RequiredOptionalParameters = new List { @@ -1756,11 +1721,11 @@ async Task> IFuturesTriggerOrderRestClient.CancelFut } }; - async Task> IFuturesTpSlRestClient.SetFuturesTpSlAsync(SetTpSlRequest request, CancellationToken ct) + async Task> IFuturesTpSlRestClient.SetFuturesTpSlAsync(SetTpSlRequest request, CancellationToken ct) { - var validationError = ((IFuturesTpSlRestClient)this).SetFuturesTpSlOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SetFuturesTpSlOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var result = await CollateralTrading.PlaceOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), @@ -1770,14 +1735,14 @@ async Task> IFuturesTpSlRestClient.SetFuturesTpSlAsy triggerPrice: request.TriggerPrice, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); // Return - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, new SharedId(result.Data.OrderId.ToString())); + return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions IFuturesTpSlRestClient.CancelFuturesTpSlOptions { get; } = new EndpointOptions(true) + EndpointOptions IFuturesTpSlRestClient.CancelFuturesTpSlOptions { get; } = new EndpointOptions(_exchange, true) { RequiredOptionalParameters = new List { @@ -1785,36 +1750,36 @@ async Task> IFuturesTpSlRestClient.SetFuturesTpSlAsy } }; - async Task> IFuturesTpSlRestClient.CancelFuturesTpSlAsync(CancelTpSlRequest request, CancellationToken ct) + async Task> IFuturesTpSlRestClient.CancelFuturesTpSlAsync(CancelTpSlRequest request, CancellationToken ct) { - var validationError = ((IFuturesTpSlRestClient)this).CancelFuturesTpSlOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.CancelFuturesTpSlOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); if (!long.TryParse(request.OrderId, out var orderId)) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid(nameof(CancelTpSlRequest.OrderId), "Invalid order id")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid(nameof(CancelTpSlRequest.OrderId), "Invalid order id")); var result = await Trading.CancelOrderAsync( request.Symbol!.GetSymbol(FormatSymbol), orderId, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); // Return - return result.AsExchangeResult(Exchange, request.Symbol.TradingMode, true); + return HttpResult.Ok(result, true); } #endregion #region Funding Rate client - GetFundingRateHistoryOptions IFundingRateRestClient.GetFundingRateHistoryOptions { get; } = new GetFundingRateHistoryOptions(false, true, true, 100, false); + GetFundingRateHistoryOptions IFundingRateRestClient.GetFundingRateHistoryOptions { get; } = new GetFundingRateHistoryOptions(_exchange, false, true, true, 100, false); - async Task> IFundingRateRestClient.GetFundingRateHistoryAsync(GetFundingRateHistoryRequest request, PageRequest? pageRequest, CancellationToken ct) + async Task> IFundingRateRestClient.GetFundingRateHistoryAsync(GetFundingRateHistoryRequest request, PageRequest? pageRequest, CancellationToken ct) { - var validationError = ((IFundingRateRestClient)this).GetFundingRateHistoryOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.GetFundingRateHistoryOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var direction = DataDirection.Descending; var limit = request.Limit ?? 100; @@ -1828,8 +1793,8 @@ async Task> IFundingRateRestClient.GetFun limit: limit, offset: pageParams.Offset, ct: ct).ConfigureAwait(false); - if (!result) - return result.AsExchangeResult(Exchange, null, default); + if (!result.Success) + return HttpResult.Fail(result); var nextPageRequest = Pagination.GetNextPageRequest( () => Pagination.NextPageFromOffset(pageParams, limit), @@ -1839,10 +1804,7 @@ async Task> IFundingRateRestClient.GetFun request.EndTime ?? DateTime.UtcNow, pageParams); - return result.AsExchangeResult( - Exchange, - request.Symbol.TradingMode, - ExchangeHelpers.ApplyFilter(result.Data, x => x.FundingTime, request.StartTime, request.EndTime, direction) + return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.FundingTime, request.StartTime, request.EndTime, direction) .Select(x => new SharedFundingRate(x.FundingRate, x.FundingTime)) .ToArray(), nextPageRequest); @@ -1851,23 +1813,23 @@ async Task> IFundingRateRestClient.GetFun #region Transfer client - TransferOptions ITransferRestClient.TransferOptions { get; } = new TransferOptions([ + TransferOptions ITransferRestClient.TransferOptions { get; } = new TransferOptions(_exchange, [ SharedAccountType.Spot, SharedAccountType.PerpetualLinearFutures, SharedAccountType.PerpetualInverseFutures, SharedAccountType.DeliveryLinearFutures, SharedAccountType.DeliveryInverseFutures ]); - async Task> ITransferRestClient.TransferAsync(TransferRequest request, CancellationToken ct) + async Task> ITransferRestClient.TransferAsync(TransferRequest request, CancellationToken ct) { - var validationError = ((ITransferRestClient)this).TransferOptions.ValidateRequest(Exchange, request, TradingMode.Spot, SupportedTradingModes); + var validationError = SharedClient.TransferOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeWebResult(Exchange, validationError); + return HttpResult.Fail(Exchange, validationError); var fromType = GetTransferType(request.FromAccountType); var toType = GetTransferType(request.ToAccountType); if (fromType == null || toType == null) - return new ExchangeWebResult(Exchange, ArgumentError.Invalid("To/From AccountType", "invalid to/from account combination")); + return HttpResult.Fail(Exchange, ArgumentError.Invalid("To/From AccountType", "invalid to/from account combination")); // Get data var transfer = await Account.TransferAsync( @@ -1876,10 +1838,10 @@ async Task> ITransferRestClient.TransferAsync(Transf request.Asset, request.Quantity, ct: ct).ConfigureAwait(false); - if (!transfer) - return transfer.AsExchangeResult(Exchange, null, default); + if (!transfer.Success) + return HttpResult.Fail(transfer); - return transfer.AsExchangeResult(Exchange, TradingMode.Spot, new SharedId("")); + return HttpResult.Ok(transfer, new SharedId("")); } private AccountType? GetTransferType(SharedAccountType type) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs index 7a90d44..9747ea1 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs @@ -26,15 +26,15 @@ internal WhiteBitRestClientV4ApiSubAccount(WhiteBitRestClientV4Api baseClient) #region Create Sub Account /// - public async Task> CreateSubAccountAsync(string alias, string? email = null, bool? shareKyc = null, string? spotEnabled = null, string? collateralEnabled = null, CancellationToken ct = default) + public async Task> CreateSubAccountAsync(string alias, string? email = null, bool? shareKyc = null, string? spotEnabled = null, string? collateralEnabled = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("alias", alias); - parameters.AddOptional("email", email); - parameters.AddOptional("shareKyc", shareKyc); - var permissions = new ParameterCollection(); - permissions.AddOptional("spotEnabled", spotEnabled); - permissions.AddOptional("collateralEnabled", collateralEnabled); + parameters.Add("email", email); + parameters.Add("shareKyc", shareKyc); + var permissions = new Parameters(WhiteBitExchange._parameterSerializationSettings); + permissions.Add("spotEnabled", spotEnabled); + permissions.Add("collateralEnabled", collateralEnabled); parameters.Add("permissions", permissions); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/create", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -47,9 +47,9 @@ public async Task> CreateSubAccountAsync(strin #region Delete Sub Account /// - public async Task DeleteSubAccountAsync(string id, CancellationToken ct = default) + public async Task DeleteSubAccountAsync(string id, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/delete", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -62,12 +62,12 @@ public async Task DeleteSubAccountAsync(string id, CancellationTo #region Edit Sub Account /// - public async Task EditSubAccountAsync(string id, string alias, string spotEnabled, string collateralEnabled, CancellationToken ct = default) + public async Task EditSubAccountAsync(string id, string alias, string spotEnabled, string collateralEnabled, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); parameters.Add("alias", alias); - var permissions = new ParameterCollection(); + var permissions = new Parameters(WhiteBitExchange._parameterSerializationSettings); permissions.Add("spotEnabled", spotEnabled); permissions.Add("collateralEnabled", collateralEnabled); parameters.Add("permissions", permissions); @@ -82,12 +82,12 @@ public async Task EditSubAccountAsync(string id, string alias, st #region Get Sub Accounts /// - public async Task> GetSubAccountsAsync(string? search = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetSubAccountsAsync(string? search = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("search", search); - parameters.AddOptional("", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("search", search); + parameters.Add("", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/list", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -99,13 +99,13 @@ public async Task> GetSubAccountsAsync(string #region Subaccount Transfer /// - public async Task SubaccountTransferAsync(string subaccountId, SubTransferDirection direction, string asset, decimal quantity, CancellationToken ct = default) + public async Task SubaccountTransferAsync(string subaccountId, SubTransferDirection direction, string asset, decimal quantity, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", subaccountId); - parameters.AddEnum("direction", direction); + parameters.Add("direction", direction); parameters.Add("ticker", asset); - parameters.AddString("amount", quantity); + parameters.Add("amount", quantity); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); ; var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -117,9 +117,9 @@ public async Task SubaccountTransferAsync(string subaccountId, Su #region Block Subaccount /// - public async Task BlockSubaccountAsync(string id, CancellationToken ct = default) + public async Task BlockSubaccountAsync(string id, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/block", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -132,9 +132,9 @@ public async Task BlockSubaccountAsync(string id, CancellationTok #region Block Subaccount /// - public async Task UnblockSubaccountAsync(string id, CancellationToken ct = default) + public async Task UnblockSubaccountAsync(string id, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/unblock", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -147,21 +147,21 @@ public async Task UnblockSubaccountAsync(string id, CancellationT #region Get Subaccount Balances /// - public async Task> GetSubaccountBalancesAsync(string id, string? asset = null, CancellationToken ct = default) + public async Task> GetSubaccountBalancesAsync(string id, string? asset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); - parameters.AddOptional("ticker", asset); + parameters.Add("ticker", asset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/balances", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data.Select(x => x.Value).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => x.Value).ToArray()); } #endregion @@ -169,9 +169,9 @@ public async Task> GetSubaccountBalancesAsy #region Get Subaccount Transfer History /// - public async Task> GetSubaccountTransferHistoryAsync(CancellationToken ct = default) + public async Task> GetSubaccountTransferHistoryAsync(CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/transfer/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs index 2e281b0..4c962d3 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs @@ -32,7 +32,7 @@ internal WhiteBitRestClientV4ApiTrading(ILogger logger, WhiteBitRestClientV4Api #region Place Order /// - public async Task> PlaceSpotOrderAsync( + public async Task> PlaceSpotOrderAsync( string symbol, OrderSide side, NewOrderType type, @@ -48,19 +48,19 @@ public async Task> PlaceSpotOrderAsync( CancellationToken ct = default) { if (quoteQuantity != null && type != NewOrderType.Market && side != OrderSide.Buy) - return new WebCallResult(ArgumentError.Invalid(nameof(quoteQuantity), "quoteQuantity parameter only supported for buy market orders")); + return HttpResult.Fail(WhiteBitExchange.ExchangeName, ArgumentError.Invalid(nameof(quoteQuantity), "quoteQuantity parameter only supported for buy market orders")); - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.AddEnum("side", side); - parameters.AddString("amount", quantity ?? quoteQuantity ?? 0); - parameters.AddOptionalString("price", price); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptional("postOnly", postOnly); - parameters.AddOptional("ioc", immediateOrCancel); - parameters.AddOptional("bboRole", bboRole); - parameters.AddOptionalString("activation_price", triggerPrice); - parameters.AddOptionalEnum("stp", stpMode); + parameters.Add("side", side); + parameters.Add("amount", quantity ?? quoteQuantity ?? 0); + parameters.Add("price", price); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("postOnly", postOnly); + parameters.Add("ioc", immediateOrCancel); + parameters.Add("bboRole", bboRole); + parameters.Add("activation_price", triggerPrice); + parameters.Add("stp", stpMode); string path; if (type == NewOrderType.Limit) @@ -102,23 +102,23 @@ public async Task> PlaceSpotOrderAsync( #region Place Multiple Order /// - public async Task[]>> PlaceSpotMultipleOrdersAsync( + public async Task[]>> PlaceSpotMultipleOrdersAsync( IEnumerable requests, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("orders", requests.ToArray()); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var resultData = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); - if (!resultData) - return resultData.As[]>(default); + if (!resultData.Success) + return HttpResult.Fail[]>(resultData); var result = new List>(); foreach (var item in resultData.Data) { if (item.Error?.Code == null) { - result.Add(new CallResult(item)); + result.Add(CallResult.Ok(item)); } else { @@ -128,14 +128,14 @@ public async Task[]>> PlaceSpotM else error = new ServerError(item.Error.Code, _baseClient.GetErrorInfo(item.Error.Code, item.Error.Message!)); - result.Add(new CallResult(error)); + result.Add(CallResult.Fail(error)); } } if (result.All(x => !x.Success)) - return resultData.AsErrorWithData(new ServerError(new ErrorInfo(ErrorType.AllOrdersFailed, "All orders failed")), result.ToArray()); + return HttpResult.Fail(resultData, new ServerError(new ErrorInfo(ErrorType.AllOrdersFailed, "All orders failed")), result.ToArray()); - return resultData.As(result.ToArray()); + return HttpResult.Ok(resultData, result.ToArray()); } #endregion @@ -143,12 +143,12 @@ public async Task[]>> PlaceSpotM #region Cancel Order /// - public async Task> CancelOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, CancellationToken ct = default) + public async Task> CancelOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.AddOptional("orderId", orderId); - parameters.AddOptional("clientOrderId", clientOrderId); + parameters.Add("orderId", orderId); + parameters.Add("clientOrderId", clientOrderId); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -160,9 +160,9 @@ public async Task> CancelOrderAsync(string symbol, #region Cancel Orders /// - public async Task> CancelOrdersAsync(IEnumerable requests, CancellationToken ct = default) + public async Task> CancelOrdersAsync(IEnumerable requests, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("orders", requests.ToArray()); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -175,11 +175,11 @@ public async Task> CancelOrdersAsync #region Cancel All Orders /// - public async Task CancelAllOrdersAsync(string? symbol = null, IEnumerable? orderProductTypes = null, CancellationToken ct = default) + public async Task CancelAllOrdersAsync(string? symbol = null, IEnumerable? orderProductTypes = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); - parameters.AddOptional("type", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); + parameters.Add("type", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel/all", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -191,14 +191,14 @@ public async Task CancelAllOrdersAsync(string? symbol = null, IEn #region Get Open Orders /// - public async Task> GetOpenOrdersAsync(string? symbol = null, long? orderId = null, string? clientOrderId = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetOpenOrdersAsync(string? symbol = null, long? orderId = null, string? clientOrderId = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); - parameters.AddOptional("orderId", orderId); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); + parameters.Add("orderId", orderId); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -210,7 +210,7 @@ public async Task> GetOpenOrdersAsync(string? sym #region Get Closed Orders /// - public async Task>> GetClosedOrdersAsync( + public async Task>> GetClosedOrdersAsync( string? symbol = null, long? orderId = null, string? clientOrderId = null, @@ -221,23 +221,23 @@ public async Task>> GetC DateTime? endTime = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); - parameters.AddOptional("orderId", orderId); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptionalEnum("status", status); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); - parameters.AddOptionalSeconds("startDate", startTime); - parameters.AddOptionalSeconds("endDate", endTime); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); + parameters.Add("orderId", orderId); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("status", status); + parameters.Add("limit", limit); + parameters.Add("offset", offset); + parameters.Add("startDate", startTime); + parameters.Add("endDate", endTime); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/order/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) + if (!result.Success) return result; if (result.Data == null) - return result.As(new Dictionary()); + return HttpResult.Ok(result, new Dictionary()); foreach (var symbolOrders in result.Data) { @@ -253,15 +253,15 @@ public async Task>> GetC #region Get User Trades /// - public async Task> GetUserTradesAsync(string? symbol = null, string? clientOrderId = null, DateTime? startDate = null, DateTime? endDate = null, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetUserTradesAsync(string? symbol = null, string? clientOrderId = null, DateTime? startDate = null, DateTime? endDate = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); - parameters.AddOptional("clientOrderId", clientOrderId); - parameters.AddOptionalSeconds("startTime", startDate); - parameters.AddOptionalSeconds("endTime", endDate); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); + parameters.Add("clientOrderId", clientOrderId); + parameters.Add("startTime", startDate); + parameters.Add("endTime", endDate); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/executed-history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); if (symbol != null) @@ -272,8 +272,8 @@ public async Task> GetUserTradesAsync(string? else { var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return HttpResult.Fail(result); foreach (var item in result.Data) { @@ -281,7 +281,7 @@ public async Task> GetUserTradesAsync(string? x.Symbol = item.Key; } - return result.As(result.Data.Values.SelectMany(x => x).OrderByDescending(x => x.Time).ToArray()); + return HttpResult.Ok(result, result.Data.Values.SelectMany(x => x).OrderByDescending(x => x.Time).ToArray()); } } @@ -290,16 +290,16 @@ public async Task> GetUserTradesAsync(string? #region Get Order Trades /// - public async Task> GetOrderTradesAsync(long orderId, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetOrderTradesAsync(long orderId, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("orderId", orderId); - parameters.AddOptional("limit", limit); - parameters.AddOptional("offset", offset); + parameters.Add("limit", limit); + parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/order", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return result.As(result.Data?.Records); + return HttpResult.Ok(result, result.Data?.Records); } #endregion @@ -307,16 +307,16 @@ public async Task> GetOrderTradesAsync(long o #region Edit Order /// - public async Task> EditOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, decimal? quantity = null, decimal? quoteQuantity = null, decimal? price = null, decimal? triggerPrice = null, CancellationToken ct = default) + public async Task> EditOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, decimal? quantity = null, decimal? quoteQuantity = null, decimal? price = null, decimal? triggerPrice = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("orderId", orderId); - parameters.AddOptional("clientOrderId", clientOrderId); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("orderId", orderId); + parameters.Add("clientOrderId", clientOrderId); parameters.Add("market", symbol); - parameters.AddOptionalString("amount", quantity); - parameters.AddOptionalString("total", quoteQuantity); - parameters.AddOptionalString("price", price); - parameters.AddOptionalString("activationPrice", triggerPrice); + parameters.Add("amount", quantity); + parameters.Add("total", quoteQuantity); + parameters.Add("price", price); + parameters.Add("activationPrice", triggerPrice); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/modify", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -328,14 +328,14 @@ public async Task> EditOrderAsync(string symbol, lo #region Set Kill Switch /// - public async Task SetKillSwitchAsync(string symbol, int timeout, IEnumerable? orderProductTypes = null, CancellationToken ct = default) + public async Task SetKillSwitchAsync(string symbol, int timeout, IEnumerable? orderProductTypes = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); #pragma warning disable CS8604 // Possible null reference argument. Works as intended parameters.Add("timeout", timeout == 0 ? null : timeout.ToString()); #pragma warning restore CS8604 // Possible null reference argument. - parameters.AddOptional("types", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); + parameters.Add("types", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/kill-switch", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); @@ -347,10 +347,10 @@ public async Task SetKillSwitchAsync(string symbol, int timeout, #region Get Kill Switch /// - public async Task> GetKillSwitchStatusAsync(string? symbol = null, CancellationToken ct = default) + public async Task> GetKillSwitchStatusAsync(string? symbol = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); - parameters.AddOptional("market", symbol); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); + parameters.Add("market", symbol); var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/kill-switch/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs index fc0ebf4..df52613 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs @@ -52,7 +52,7 @@ internal partial class WhiteBitSocketClientV4Api : SocketApiClient internal WhiteBitSocketClientV4Api(ILogger logger, WhiteBitSocketOptions options) : - base(logger, options.Environment.SocketClientAddress!, options, options.V4Options) + base(logger, WhiteBitExchange.ExchangeName, options.Environment.SocketClientAddress!, options, options.V4Options) { RateLimiter = WhiteBitExchange.RateLimiter.WhiteBitSocket; AllowTopicsOnTheSameConnection = false; @@ -87,7 +87,7 @@ protected override WhiteBitAuthenticationProvider CreateAuthenticationProvider(W #region Trades /// - public async Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default) + public async Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default) { return await QueryAsync( "trades_request", @@ -99,11 +99,11 @@ public async Task> GetTradeHistoryAsync(string } /// - public async Task> SubscribeToTradeUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTradeUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) => await SubscribeToTradeUpdatesAsync([symbol], onMessage, ct).ConfigureAwait(false); /// - public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -129,22 +129,25 @@ public async Task> SubscribeToTradeUpdatesAsync(I #region Last Price /// - public async Task> GetLastPriceAsync(string symbol, CancellationToken ct = default) + public async Task> GetLastPriceAsync(string symbol, CancellationToken ct = default) { var result = await QueryAsync( "lastprice_request", false, ct, symbol).ConfigureAwait(false); - return result.As(result.Data ?? default); + if (!result.Success) + return WebSocketResult.Fail(result); + + return WebSocketResult.Ok(result, result.Data ?? default); } /// - public async Task> SubscribeToLastPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToLastPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) => await SubscribeToLastPriceUpdatesAsync([symbol], onMessage, ct).ConfigureAwait(false); /// - public async Task> SubscribeToLastPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToLastPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -165,7 +168,7 @@ public async Task> SubscribeToLastPriceUpdatesAsy #region Ticker /// - public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) + public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) { return await QueryAsync( "market_request", @@ -176,11 +179,11 @@ public async Task> GetTickerAsync(string symbol } /// - public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) => await SubscribeToTickerUpdatesAsync([symbol], onMessage, ct).ConfigureAwait(false); /// - public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -201,7 +204,7 @@ public async Task> SubscribeToTickerUpdatesAsync( #region Book Ticker /// - public async Task> SubscribeToBookTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToBookTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -223,7 +226,7 @@ public async Task> SubscribeToBookTickerUpdatesAs } /// - public async Task> SubscribeToBookTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToBookTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -249,7 +252,7 @@ public async Task> SubscribeToBookTickerUpdatesAs #region Kline /// - public async Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default) + public async Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default) { return await QueryAsync( "candles_request", @@ -262,7 +265,7 @@ public async Task> GetKlinesAsync(string symbo } /// - public async Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> onMessage, CancellationToken ct = default) { var subscription = new WhiteBitKlineSubscription(_logger, this, symbol, interval, onMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -273,7 +276,7 @@ public async Task> SubscribeToKlineUpdatesAsync(s #region Order book /// - public async Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default) + public async Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default) { depth.ValidateIntBetween(nameof(depth), 0, 100); @@ -287,7 +290,7 @@ public async Task> GetOrderBookAsync(string symbol } /// - public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, Action> onMessage, CancellationToken ct = default) { depth.ValidateIntValues(nameof(depth), 1, 5, 10, 20, 30, 50, 100); @@ -300,24 +303,24 @@ public async Task> SubscribeToOrderBookUpdatesAsy #region Spot Balances /// - public async Task> GetSpotBalancesAsync(CancellationToken ct = default) + public async Task> GetSpotBalancesAsync(CancellationToken ct = default) { var result = await QueryAsync>( "balanceSpot_request", true, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return WebSocketResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data.Values.ToArray()); + return WebSocketResult.Ok(result, result.Data.Values.ToArray()); } /// - public async Task> SubscribeToSpotBalanceUpdatesAsync(IEnumerable assets, Action>> onMessage, CancellationToken ct = default) + public async Task> SubscribeToSpotBalanceUpdatesAsync(IEnumerable assets, Action>> onMessage, CancellationToken ct = default) { var subscription = new WhiteBitSpotBalanceSubscription(_logger, this, assets.ToArray(), onMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -327,24 +330,24 @@ public async Task> SubscribeToSpotBalanceUpdatesA #region Margin Balances /// - public async Task> GetMarginBalancesAsync(CancellationToken ct = default) + public async Task> GetMarginBalancesAsync(CancellationToken ct = default) { var result = await QueryAsync>( "balanceMargin_request", true, ct).ConfigureAwait(false); - if (!result) - return result.As(default); + if (!result.Success) + return WebSocketResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return result.As(result.Data.Values.ToArray()); + return WebSocketResult.Ok(result, result.Data.Values.ToArray()); } /// - public async Task> SubscribeToMarginBalanceUpdatesAsync(IEnumerable assets, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToMarginBalanceUpdatesAsync(IEnumerable assets, Action> onMessage, CancellationToken ct = default) { var subscription = new WhiteBitMarginBalanceSubscription(_logger, this, assets.ToArray(), onMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -354,7 +357,7 @@ public async Task> SubscribeToMarginBalanceUpdate #region Open Orders /// - public async Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "ordersPending_request", @@ -366,7 +369,7 @@ public async Task> GetOpenOrdersAsync(string symbol, } /// - public async Task> SubscribeToOpenOrderUpdatesAsync(IEnumerable symbols, Action> onOrderMessage, Action>? onOtoOrdersMessage = null, CancellationToken ct = default) + public async Task> SubscribeToOpenOrderUpdatesAsync(IEnumerable symbols, Action> onOrderMessage, Action>? onOtoOrdersMessage = null, CancellationToken ct = default) { var subscription = new WhiteBitOpenOrderSubscription(_logger, this, symbols.ToArray(), onOrderMessage, onOtoOrdersMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -376,7 +379,7 @@ public async Task> SubscribeToOpenOrderUpdatesAsy #region Closed Orders /// - public async Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "ordersExecuted_request", @@ -392,7 +395,7 @@ public async Task> GetClosedOrdersAsync(string } /// - public async Task> SubscribeToClosedOrderUpdatesAsync(IEnumerable symbols, ClosedOrderFilter filter, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToClosedOrderUpdatesAsync(IEnumerable symbols, ClosedOrderFilter filter, Action> onMessage, CancellationToken ct = default) { var subscription = new WhiteBitClosedOrderSubscription(_logger, this, symbols.ToArray(), (int)filter, onMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -402,7 +405,7 @@ public async Task> SubscribeToClosedOrderUpdatesA #region User Trades /// - public async Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "deals_request", @@ -414,7 +417,7 @@ public async Task> GetUserTradesAsync(string symb } /// - public async Task> SubscribeToUserTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToUserTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default) { var subscription = new WhiteBitUserTradeSubscription(_logger, this, symbols.ToArray(), onMessage); return await SubscribeAsync(BaseAddress.AppendPath("ws"), subscription, ct).ConfigureAwait(false); @@ -424,7 +427,7 @@ public async Task> SubscribeToUserTradeUpdatesAsy #region Positions /// - public async Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -448,7 +451,7 @@ public async Task> SubscribeToPositionUpdatesAsyn #region Borrow /// - public async Task> SubscribeToBorrowUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToBorrowUpdatesAsync(Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -471,7 +474,7 @@ public async Task> SubscribeToBorrowUpdatesAsync( #region Account Borrow Events /// - public async Task> SubscribeToAccountMarginPositionEventUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToAccountMarginPositionEventUpdatesAsync(Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -493,7 +496,7 @@ public async Task> SubscribeToAccountMarginPositi #region Account Borrow Events /// - public async Task> SubscribeToAccountBorrowEventUpdatesAsync(Action> onMessage, CancellationToken ct = default) + public async Task> SubscribeToAccountBorrowEventUpdatesAsync(Action> onMessage, CancellationToken ct = default) { var internalHandler = new Action>((receiveTime, originalData, invocations, data) => { @@ -513,7 +516,7 @@ public async Task> SubscribeToAccountBorrowEventU } #endregion - private async Task> QueryAsync(string method, bool auth, CancellationToken ct, params object[] parameters) + private async Task> QueryAsync(string method, bool auth, CancellationToken ct, params object[] parameters) { var query = new WhiteBitQuery(this, new WhiteBitSocketRequest { @@ -523,14 +526,14 @@ private async Task> QueryAsync(string method, bool auth, Cancel }, auth); var result = await QueryAsync(BaseAddress.AppendPath("ws"), query, ct).ConfigureAwait(false); - return result.As(result.Data == null ? default : result.Data.Result); + return WebSocketResult.Ok(result, result.Data == null ? default : result.Data.Result); } /// protected override async Task GetAuthenticationRequestAsync(SocketConnection connection) { var token = await GetTokenAsync().ConfigureAwait(false); - if (!token) + if (!token.Success) return null; return new WhiteBitQuery(this, new WhiteBitSocketRequest @@ -544,16 +547,16 @@ private async Task> QueryAsync(string method, bool auth, Cancel }, false); } - private async Task> GetTokenAsync() + private async Task> GetTokenAsync() { if (ApiCredentials == null) - return new CallResult(new NoApiCredentialsError()); + return WebSocketResult.Fail(ExchangeName, new NoApiCredentialsError()); if (_tokenCache.TryGetValue(ApiCredentials.Key, out var token) && token.Expire > DateTime.UtcNow) - return new CallResult(token.Token); + return new WebSocketResult(ExchangeName, token.Token, null); if (ClientOptions.Environment.Name == "UnitTest") - return new CallResult("123"); + return new WebSocketResult(ExchangeName, "123", null); _logger.LogDebug("Requesting websocket token"); var restClient = new WhiteBitRestClient(x => @@ -563,14 +566,14 @@ private async Task> GetTokenAsync() }); var result = await ((WhiteBitRestClientV4ApiAccount)restClient.V4Api.Account).GetWebsocketTokenAsync().ConfigureAwait(false); - if (!result) + if (!result.Success) { _logger.LogWarning("Failed to retrieve websocket token: {Error}", result.Error); - return result.As(default); + return WebSocketResult.Fail(ExchangeName, result.Error!); } _tokenCache[ApiCredentials.Key] = new CachedToken { Token = result.Data, Expire = DateTime.UtcNow.AddSeconds(60) }; - return result.As(result.Data); + return new WebSocketResult(ExchangeName, result.Data, null); } private class CachedToken diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs index e9aa741..1fc6100 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs @@ -16,9 +16,10 @@ namespace WhiteBit.Net.Clients.V4Api { internal partial class WhiteBitSocketClientV4Api : IWhiteBitSocketClientV4ApiShared { + private const string _exchange = "WhiteBit"; private const string _topicSpotId = "WhiteBitSpot"; private const string _topicFuturesId = "WhiteBitFutures"; - public string Exchange => "WhiteBit"; + public string Exchange => _exchange; public TradingMode[] SupportedTradingModes => new[] { TradingMode.Spot, TradingMode.PerpetualLinear }; public TradingMode[] SupportedFuturesModes => new[] { TradingMode.PerpetualLinear }; @@ -27,22 +28,22 @@ internal partial class WhiteBitSocketClientV4Api : IWhiteBitSocketClientV4ApiSha public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); #region Balance client - EndpointOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new EndpointOptions(true) + EndpointOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new EndpointOptions(_exchange, true) { OptionalExchangeParameters = new List { new ParameterDescription("BalanceAssets", typeof(List), "The assets to subscribe for updates", new List{ "USDT", "ETH", "BTC" }) } }; - async Task> IBalanceSocketClient.SubscribeToBalanceUpdatesAsync(SubscribeBalancesRequest request, Action> handler, CancellationToken ct) + async Task> IBalanceSocketClient.SubscribeToBalanceUpdatesAsync(SubscribeBalancesRequest request, Action> handler, CancellationToken ct) { - var validationError = ((IBalanceSocketClient)this).SubscribeBalanceOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeBalanceOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); if (request.TradingMode == null || request.TradingMode == TradingMode.Spot) { - var assets = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); + var assets = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); if (assets == null) { // request all assets @@ -51,8 +52,8 @@ async Task> IBalanceSocketClient.SubscribeToB x.Environment = ClientOptions.Environment; }); var assetsResult = await client.V4Api.ExchangeData.GetAssetsAsync().ConfigureAwait(false); - if (!assetsResult) - return new ExchangeResult(Exchange, assetsResult.Error!); + if (!assetsResult.Success) + return WebSocketResult.Fail(Exchange, assetsResult.Error!); assets = assetsResult.Data.Where(x => x.CanDeposit).Select(x => x.Asset).ToList(); } @@ -61,11 +62,11 @@ async Task> IBalanceSocketClient.SubscribeToB assets!, update => handler(update.ToType(update.Data.Select(x => new SharedBalance(x.Key, x.Value.Available, x.Value.Available + x.Value.Frozen)).ToArray())), ct: ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } else { - var assets = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); + var assets = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); if (assets == null) { // request all assets @@ -75,8 +76,8 @@ async Task> IBalanceSocketClient.SubscribeToB x.ApiCredentials = (WhiteBitCredentials?)AuthenticationProvider!.ApiCredentials.Copy(); }); var assetsResult = await client.V4Api.Account.GetCollateralBalancesAsync().ConfigureAwait(false); - if (!assetsResult) - return new ExchangeResult(Exchange, assetsResult.Error!); + if (!assetsResult.Success) + return WebSocketResult.Fail(Exchange, assetsResult.Error!); assets = assetsResult.Data.Select(x => x.Key).Distinct().ToList(); } @@ -85,7 +86,7 @@ async Task> IBalanceSocketClient.SubscribeToB assets, update => handler(update.ToType(update.Data.Select(x => new SharedBalance(x.Asset, x.AvailableWithoutBorrow, x.Balance)).ToArray())), ct: ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } } @@ -93,12 +94,12 @@ async Task> IBalanceSocketClient.SubscribeToB #region Book Ticker client - EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(false); - async Task> IBookTickerSocketClient.SubscribeToBookTickerUpdatesAsync(SubscribeBookTickerRequest request, Action> handler, CancellationToken ct) + EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(_exchange, false); + async Task> IBookTickerSocketClient.SubscribeToBookTickerUpdatesAsync(SubscribeBookTickerRequest request, Action> handler, CancellationToken ct) { - var validationError = ((IBookTickerSocketClient)this).SubscribeBookTickerOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeBookTickerOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); var symbol = request.Symbol!.GetSymbol(FormatSymbol); var result = await SubscribeToBookTickerUpdatesAsync(symbol, update => @@ -112,13 +113,13 @@ async Task> IBookTickerSocketClient.Subscribe update.Data.BestBidQuantity))); }, ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Kline client - SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(false, + SubscribeKlineOptions IKlineSocketClient.SubscribeKlineOptions { get; } = new SubscribeKlineOptions(_exchange, false, SharedKlineInterval.OneMinute, SharedKlineInterval.ThreeMinutes, SharedKlineInterval.FiveMinutes, @@ -132,16 +133,13 @@ async Task> IBookTickerSocketClient.Subscribe SharedKlineInterval.OneDay, SharedKlineInterval.OneWeek, SharedKlineInterval.OneMonth); - async Task> IKlineSocketClient.SubscribeToKlineUpdatesAsync(SubscribeKlineRequest request, Action> handler, CancellationToken ct) + async Task> IKlineSocketClient.SubscribeToKlineUpdatesAsync(SubscribeKlineRequest request, Action> handler, CancellationToken ct) { - var interval = (Enums.KlineInterval)request.Interval; - if (!Enum.IsDefined(typeof(Enums.KlineInterval), interval)) - return new ExchangeResult(Exchange, ArgumentError.Invalid(nameof(GetKlinesRequest.Interval), "Interval not supported")); - - var validationError = ((IKlineSocketClient)this).SubscribeKlineOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeKlineOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); + var interval = (Enums.KlineInterval)request.Interval; var symbol = request.Symbol!.GetSymbol(FormatSymbol); var result = await SubscribeToKlineUpdatesAsync(symbol, interval, update => { @@ -152,20 +150,20 @@ async Task> IKlineSocketClient.SubscribeToKli handler(update.ToType(new SharedKline(request.Symbol, symbol, item.OpenTime, item.ClosePrice, item.HighPrice, item.LowPrice, item.OpenPrice, item.Volume))); }, ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Ticker client - SubscribeTickerOptions ITickerSocketClient.SubscribeTickerOptions { get; } = new SubscribeTickerOptions() + SubscribeTickerOptions ITickerSocketClient.SubscribeTickerOptions { get; } = new SubscribeTickerOptions(_exchange) { SupportsMultipleSymbols = true }; - async Task> ITickerSocketClient.SubscribeToTickerUpdatesAsync(SubscribeTickerRequest request, Action> handler, CancellationToken ct) + async Task> ITickerSocketClient.SubscribeToTickerUpdatesAsync(SubscribeTickerRequest request, Action> handler, CancellationToken ct) { - var validationError = ((ITickerSocketClient)this).SubscribeTickerOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeTickerOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); var symbols = request.Symbols?.Length > 0 ? request.Symbols.Select(x => x.GetSymbol(FormatSymbol)).ToArray() : [request.Symbol!.GetSymbol(FormatSymbol)]; var result = await SubscribeToTickerUpdatesAsync(symbols, update => handler(update.ToType( @@ -174,22 +172,22 @@ async Task> ITickerSocketClient.SubscribeToTi QuoteVolume = update.Data.Ticker.QuoteVolume })), ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Trade client - EndpointOptions ITradeSocketClient.SubscribeTradeOptions { get; } = new EndpointOptions(false) + EndpointOptions ITradeSocketClient.SubscribeTradeOptions { get; } = new EndpointOptions(_exchange, false) { SupportsMultipleSymbols = true, MaxSymbolCount = 100 }; - async Task> ITradeSocketClient.SubscribeToTradeUpdatesAsync(SubscribeTradeRequest request, Action> handler, CancellationToken ct) + async Task> ITradeSocketClient.SubscribeToTradeUpdatesAsync(SubscribeTradeRequest request, Action> handler, CancellationToken ct) { - var validationError = ((ITradeSocketClient)this).SubscribeTradeOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeTradeOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); var symbols = request.Symbols?.Length > 0 ? request.Symbols.Select(x => x.GetSymbol(FormatSymbol)).ToArray() : [request.Symbol!.GetSymbol(FormatSymbol)]; var result = await SubscribeToTradeUpdatesAsync(symbols, @@ -205,27 +203,27 @@ async Task> ITradeSocketClient.SubscribeToTra } ).ToArray())); }, ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region User Trade client - EndpointOptions IUserTradeSocketClient.SubscribeUserTradeOptions { get; } = new EndpointOptions(true) + EndpointOptions IUserTradeSocketClient.SubscribeUserTradeOptions { get; } = new EndpointOptions(_exchange, true) { OptionalExchangeParameters = new List { new ParameterDescription("UserTradeSymbols", typeof(List), "The symbols to subscribe for updates", new List{ "ETH_USDT", "ETH_PERP" }) } }; - async Task> IUserTradeSocketClient.SubscribeToUserTradeUpdatesAsync(SubscribeUserTradeRequest request, Action> handler, CancellationToken ct) + async Task> IUserTradeSocketClient.SubscribeToUserTradeUpdatesAsync(SubscribeUserTradeRequest request, Action> handler, CancellationToken ct) { - var validationError = ((IUserTradeSocketClient)this).SubscribeUserTradeOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedTradingModes); + var validationError = SharedClient.SubscribeUserTradeOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "UserTradeSymbols"); + var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "UserTradeSymbols"); if (symbols == null) { // request all symbols @@ -234,8 +232,8 @@ async Task> IUserTradeSocketClient.SubscribeT x.Environment = ClientOptions.Environment; }); var symbolsResult = await client.V4Api.ExchangeData.GetSymbolsAsync().ConfigureAwait(false); - if (!symbolsResult) - return new ExchangeResult(Exchange, symbolsResult.Error!); + if (!symbolsResult.Success) + return WebSocketResult.Fail(Exchange, symbolsResult.Error!); symbols = symbolsResult.Data.Select(x => x.Name).ToList(); } @@ -265,27 +263,27 @@ async Task> IUserTradeSocketClient.SubscribeT }])); }, ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Spot Order client - EndpointOptions ISpotOrderSocketClient.SubscribeSpotOrderOptions { get; } = new EndpointOptions(false) + EndpointOptions ISpotOrderSocketClient.SubscribeSpotOrderOptions { get; } = new EndpointOptions(_exchange, false) { OptionalExchangeParameters = new List { new ParameterDescription("OrderSymbols", typeof(List), "The symbols to subscribe for updates", new List{ "ETH_USDT" }) } }; - async Task> ISpotOrderSocketClient.SubscribeToSpotOrderUpdatesAsync(SubscribeSpotOrderRequest request, Action> handler, CancellationToken ct) + async Task> ISpotOrderSocketClient.SubscribeToSpotOrderUpdatesAsync(SubscribeSpotOrderRequest request, Action> handler, CancellationToken ct) { - var validationError = ((ISpotOrderSocketClient)this).SubscribeSpotOrderOptions.ValidateRequest(Exchange, request, TradingMode.Spot, [TradingMode.Spot]); + var validationError = SharedClient.SubscribeSpotOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); + var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); if (symbols == null) { // request all symbols @@ -294,8 +292,8 @@ async Task> ISpotOrderSocketClient.SubscribeT x.Environment = ClientOptions.Environment; }); var symbolsResult = await client.V4Api.ExchangeData.GetSymbolsAsync().ConfigureAwait(false); - if (!symbolsResult) - return new ExchangeResult(Exchange, symbolsResult.Error!); + if (!symbolsResult.Success) + return WebSocketResult.Fail(Exchange, symbolsResult.Error!); symbols = symbolsResult.Data.Where(x => x.SymbolType == SymbolType.Spot).Select(x => x.Name).ToList(); } @@ -339,17 +337,17 @@ async Task> ISpotOrderSocketClient.SubscribeT }, ct: ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Position client - EndpointOptions IPositionSocketClient.SubscribePositionOptions { get; } = new EndpointOptions(false); - async Task> IPositionSocketClient.SubscribeToPositionUpdatesAsync(SubscribePositionRequest request, Action> handler, CancellationToken ct) + EndpointOptions IPositionSocketClient.SubscribePositionOptions { get; } = new EndpointOptions(_exchange, false); + async Task> IPositionSocketClient.SubscribeToPositionUpdatesAsync(SubscribePositionRequest request, Action> handler, CancellationToken ct) { - var validationError = ((IPositionSocketClient)this).SubscribePositionOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.SubscribePositionOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); var result = await SubscribeToPositionUpdatesAsync( update => @@ -368,27 +366,27 @@ async Task> IPositionSocketClient.SubscribeTo }, ct: ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion #region Futures Order client - EndpointOptions IFuturesOrderSocketClient.SubscribeFuturesOrderOptions { get; } = new EndpointOptions(false) + EndpointOptions IFuturesOrderSocketClient.SubscribeFuturesOrderOptions { get; } = new EndpointOptions(_exchange, false) { OptionalExchangeParameters = new List { new ParameterDescription("OrderSymbols", typeof(List), "The symbols to subscribe for updates", new List{ "ETH_PERP" }) } }; - async Task> IFuturesOrderSocketClient.SubscribeToFuturesOrderUpdatesAsync(SubscribeFuturesOrderRequest request, Action> handler, CancellationToken ct) + async Task> IFuturesOrderSocketClient.SubscribeToFuturesOrderUpdatesAsync(SubscribeFuturesOrderRequest request, Action> handler, CancellationToken ct) { - var validationError = ((IFuturesOrderSocketClient)this).SubscribeFuturesOrderOptions.ValidateRequest(Exchange, request, request.TradingMode, SupportedFuturesModes); + var validationError = SharedClient.SubscribeFuturesOrderOptions.ValidateRequest(request, this); if (validationError != null) - return new ExchangeResult(Exchange, validationError); + return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); + var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); if (symbols == null) { // request all symbols @@ -397,8 +395,8 @@ async Task> IFuturesOrderSocketClient.Subscri x.Environment = ClientOptions.Environment; }); var symbolsResult = await client.V4Api.ExchangeData.GetSymbolsAsync().ConfigureAwait(false); - if (!symbolsResult) - return new ExchangeResult(Exchange, symbolsResult.Error!); + if (!symbolsResult.Success) + return WebSocketResult.Fail(Exchange, symbolsResult.Error!); symbols = symbolsResult.Data.Where(x => x.SymbolType == SymbolType.Futures).Select(x => x.Name).ToList(); } @@ -443,7 +441,7 @@ async Task> IFuturesOrderSocketClient.Subscri }, ct: ct).ConfigureAwait(false); - return new ExchangeResult(Exchange, result); + return result; } #endregion diff --git a/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs b/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs index 9d3fa55..f6b630d 100644 --- a/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs +++ b/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using CryptoExchange.Net.Objects; using WhiteBit.Net.Enums; using WhiteBit.Net.Objects.Internal; using WhiteBit.Net.Objects.Models; @@ -17,6 +18,7 @@ namespace WhiteBit.Net.Converters [JsonSerializable(typeof(WhiteBitSocketResponse))] [JsonSerializable(typeof(WhiteBitSocketResponse>))] [JsonSerializable(typeof(WhiteBitSocketResponse>))] + [JsonSerializable(typeof(Parameters))] [JsonSerializable(typeof(WhiteBitSocketResponse))] [JsonSerializable(typeof(WhiteBitSocketResponse))] [JsonSerializable(typeof(WhiteBitSocketResponse))] diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiAccount.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiAccount.cs index 1b19473..4d02509 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiAccount.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiAccount.cs @@ -23,7 +23,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// /// Cancellation token - Task> GetMainBalancesAsync(CancellationToken ct = default); + Task> GetMainBalancesAsync(CancellationToken ct = default); /// /// Get deposit address @@ -37,7 +37,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["ticker"] The asset /// ["network"] Network to use /// Cancellation token - Task> GetDepositAddressAsync(string asset, string? network = null, CancellationToken ct = default); + Task> GetDepositAddressAsync(string asset, string? network = null, CancellationToken ct = default); /// /// Get spot trading balances @@ -50,7 +50,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// ["ticker"] Filter by asset /// Cancellation token - Task> GetSpotBalancesAsync(string? asset = null, CancellationToken ct = default); + Task> GetSpotBalancesAsync(string? asset = null, CancellationToken ct = default); /// /// Get fiat deposit url, note that his endpoint is not available by default and has to be activated for you by WhiteBit support @@ -77,7 +77,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["customer.address.zipCode"] Customer zip code /// ["customer.address.countryCode"] Customer country code /// Cancellation token - Task> GetFiatDepositAddressAsync(string asset, string provider, decimal quantity, string clientOrderId, string? successLink = null, string? failureLink = null, string? returnLink = null, string? customerFirstName = null, string? customerLastName = null, string? customerEmail = null, string? customerAddressLine1 = null, string? customerAddressLine2 = null, string? customerCity = null, string? customerZipCode = null, string? customerCountryCode = null, CancellationToken ct = default); + Task> GetFiatDepositAddressAsync(string asset, string provider, decimal quantity, string clientOrderId, string? successLink = null, string? failureLink = null, string? returnLink = null, string? customerFirstName = null, string? customerLastName = null, string? customerEmail = null, string? customerAddressLine1 = null, string? customerAddressLine2 = null, string? customerCity = null, string? customerZipCode = null, string? customerCountryCode = null, CancellationToken ct = default); /// /// Withdraw fiat or crypto @@ -105,7 +105,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["beneficiary.phone"] Beneficiary phone number /// ["beneficiary.email"] Beneficiary email /// Cancellation token - Task WithdrawAsync(string asset, decimal quantity, string address, string uniqueId, bool deductFeeFromOutput, string? memo = null, string? provider = null, string? network = null, bool? partialEnable = null, string? beneficiaryFirstName = null, string? beneficiaryLastName = null, string? beneficiaryTin = null, string? beneficiaryPhone = null, string? beneficiaryEmail = null, CancellationToken ct = default); + Task WithdrawAsync(string asset, decimal quantity, string address, string uniqueId, bool deductFeeFromOutput, string? memo = null, string? provider = null, string? network = null, bool? partialEnable = null, string? beneficiaryFirstName = null, string? beneficiaryLastName = null, string? beneficiaryTin = null, string? beneficiaryPhone = null, string? beneficiaryEmail = null, CancellationToken ct = default); /// /// Transfer between accounts @@ -121,7 +121,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["ticker"] The asset, for example `ETH` /// ["amount"] Quantity to transfer /// Cancellation token - Task TransferAsync(AccountType fromAccount, AccountType toAccount, string asset, decimal quantity, CancellationToken ct = default); + Task TransferAsync(AccountType fromAccount, AccountType toAccount, string asset, decimal quantity, CancellationToken ct = default); /// /// Get deposit/withdrawal history @@ -141,7 +141,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetDepositWithdrawalHistoryAsync(TransactionType? type = null, string? asset = null, string? address = null, string? memo = null, string? addresses = null, string? uniqueId = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetDepositWithdrawalHistoryAsync(TransactionType? type = null, string? asset = null, string? address = null, string? memo = null, string? addresses = null, string? uniqueId = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Create a new deposit address, note that his endpoint is not available by default and has to be activated for you by WhiteBit support @@ -156,7 +156,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["network"] Asset network /// ["type"] Address type for BTC/LTC: p2sh-segwit, bech32 /// Cancellation token - Task> CreateDepositAddressAsync(string asset, string? network = null, string? addressType = null, CancellationToken ct = default); + Task> CreateDepositAddressAsync(string asset, string? network = null, string? addressType = null, CancellationToken ct = default); /// /// Get deposit/withdrawal settings @@ -168,7 +168,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// /// Cancellation token - Task> GetDepositWithdrawalSettingsAsync(CancellationToken ct = default); + Task> GetDepositWithdrawalSettingsAsync(CancellationToken ct = default); /// /// Get mining rewards history @@ -185,7 +185,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetMiningRewardHistoryAsync(string? accountName = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetMiningRewardHistoryAsync(string? accountName = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Get collateral balances @@ -198,7 +198,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// ["ticker"] Filter by asset, for example `ETH` /// Cancellation token - Task>> GetCollateralBalancesAsync(string? asset = null, CancellationToken ct = default); + Task>> GetCollateralBalancesAsync(string? asset = null, CancellationToken ct = default); /// /// Get collateral balance summary @@ -210,7 +210,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// /// Cancellation token - Task> GetCollateralBalanceSummaryAsync(CancellationToken ct = default); + Task> GetCollateralBalanceSummaryAsync(CancellationToken ct = default); /// /// Get collateral account summary @@ -222,7 +222,7 @@ public interface IWhiteBitRestClientV4ApiAccount /// /// /// Cancellation token - Task> GetCollateralAccountSummaryAsync(CancellationToken ct = default); + Task> GetCollateralAccountSummaryAsync(CancellationToken ct = default); /// /// Asynchronously retrieves the account funding history for the specified trading pair. @@ -242,9 +242,9 @@ public interface IWhiteBitRestClientV4ApiAccount /// ["limit"] The optional maximum number of records to return. If not specified, a default limit is applied. /// ["offset"] The optional offset for pagination. Specifies the number of records to skip before returning results. /// A cancellation token that can be used to cancel the asynchronous operation. - /// A task that represents the asynchronous operation. The task result contains a WebCallResult with the funding + /// A task that represents the asynchronous operation. The task result contains a HttpResult with the funding /// history records for the specified account and symbol. - Task> GetAccountFundingHistoryAsync( + Task> GetAccountFundingHistoryAsync( string symbol, DateTime? startTime = null, DateTime? endTime = null, @@ -263,7 +263,7 @@ Task> GetAccountFundingHistoryAsy /// /// ["leverage"] New leverage setting /// Cancellation token - Task> SetAccountLeverageAsync(int leverage, CancellationToken ct = default); + Task> SetAccountLeverageAsync(int leverage, CancellationToken ct = default); /// /// Get the users trading fees @@ -275,7 +275,7 @@ Task> GetAccountFundingHistoryAsy /// /// /// Cancellation token - Task> GetTradingFeesAsync(CancellationToken ct = default); + Task> GetTradingFeesAsync(CancellationToken ct = default); /// /// Get current hedge mode status @@ -287,7 +287,7 @@ Task> GetAccountFundingHistoryAsy /// /// /// Cancellation token - Task> GetHedgeModeAsync(CancellationToken ct = default); + Task> GetHedgeModeAsync(CancellationToken ct = default); /// /// Set hedge mode @@ -300,7 +300,7 @@ Task> GetAccountFundingHistoryAsy /// /// ["hedgeMode"] Hedge mode enabled or not /// Cancellation token - Task> SetHedgeModeAsync(bool enableHedgeMode, CancellationToken ct = default); + Task> SetHedgeModeAsync(bool enableHedgeMode, CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCodes.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCodes.cs index e6950af..fa4d598 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCodes.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCodes.cs @@ -24,7 +24,7 @@ public interface IWhiteBitRestClientV4ApiCodes /// Passphrase for applying the code /// Description /// Cancellation token - Task> CreateCodeAsync(string asset, decimal quantity, string? passphrase = null, string? description = null, CancellationToken ct = default); + Task> CreateCodeAsync(string asset, decimal quantity, string? passphrase = null, string? description = null, CancellationToken ct = default); /// /// Apply a WhiteBit code @@ -38,7 +38,7 @@ public interface IWhiteBitRestClientV4ApiCodes /// The WhiteBit code /// Code passphrase /// Cancellation token - Task> ApplyCodeAsync(string code, string? passphrase = null, CancellationToken ct = default); + Task> ApplyCodeAsync(string code, string? passphrase = null, CancellationToken ct = default); /// /// Get generated code history @@ -52,7 +52,7 @@ public interface IWhiteBitRestClientV4ApiCodes /// Max number of results /// Result offset /// Cancellation token - Task> GetCreatedCodesAsync(int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetCreatedCodesAsync(int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Get account code history @@ -66,7 +66,7 @@ public interface IWhiteBitRestClientV4ApiCodes /// Max number of results /// Result offset /// Cancellation token - Task> GetCodeHistoryAsync(int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetCodeHistoryAsync(int? limit = null, int? offset = null, CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCollateralTrading.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCollateralTrading.cs index 7f2b7b4..f716c57 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCollateralTrading.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiCollateralTrading.cs @@ -43,7 +43,7 @@ public interface IWhiteBitRestClientV4ApiCollateralTrading /// ["positionSide"] Position side, required when in hedge mode /// ["reduceOnly"] Reduce only order /// Cancellation token - Task> PlaceOrderAsync( + Task> PlaceOrderAsync( string symbol, OrderSide side, NewOrderType type, @@ -72,7 +72,7 @@ Task> PlaceOrderAsync( /// /// ["market"] Filter by symbol /// Cancellation token - Task> GetOpenPositionsAsync(string? symbol = null, CancellationToken ct = default); + Task> GetOpenPositionsAsync(string? symbol = null, CancellationToken ct = default); /// /// Get position history @@ -90,7 +90,7 @@ Task> PlaceOrderAsync( /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetPositionHistoryAsync(string? symbol = null, long? positionId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetPositionHistoryAsync(string? symbol = null, long? positionId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Get open conditional orders @@ -105,7 +105,7 @@ Task> PlaceOrderAsync( /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetOpenConditionalOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetOpenConditionalOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Place a new OCO order @@ -126,7 +126,7 @@ Task> PlaceOrderAsync( /// ["positionSide"] Position side, required when in hedge mode /// ["reduceOnly"] Reduce only /// Cancellation token - Task> PlaceOcoOrderAsync( + Task> PlaceOcoOrderAsync( string symbol, OrderSide orderSide, decimal quantity, @@ -150,7 +150,7 @@ Task> PlaceOcoOrderAsync( /// ["market"] The symbol, for example `ETH_PERP` /// ["orderId"] Order id /// Cancellation token - Task> CancelOcoOrderAsync(string symbol, long orderId, CancellationToken ct = default); + Task> CancelOcoOrderAsync(string symbol, long orderId, CancellationToken ct = default); /// /// Cancel a conditional order @@ -164,7 +164,7 @@ Task> PlaceOcoOrderAsync( /// ["market"] The symbol, for example `ETH_PERP` /// ["id"] Order id /// Cancellation token - Task CancelConditionalOrderAsync(string symbol, long orderId, CancellationToken ct = default); + Task CancelConditionalOrderAsync(string symbol, long orderId, CancellationToken ct = default); /// /// Cancel an OTO order @@ -178,7 +178,7 @@ Task> PlaceOcoOrderAsync( /// ["market"] The symbol, for example `ETH_PERP` /// ["otoId"] Order id /// Cancellation token - Task CancelOTOOrderAsync(string symbol, long orderId, CancellationToken ct = default); + Task CancelOTOOrderAsync(string symbol, long orderId, CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiConvert.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiConvert.cs index 23e221e..9a50b0b 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiConvert.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiConvert.cs @@ -25,7 +25,7 @@ public interface IWhiteBitRestClientV4ApiConvert /// ["amount"] Quantity /// ["direction"] Whether the quantity is specified in the fromAsset or toAsset /// Cancellation token - Task> GetConvertEstimateAsync(string fromAsset, string toAsset, decimal quantity, string fromOrToQuantity, CancellationToken ct = default); + Task> GetConvertEstimateAsync(string fromAsset, string toAsset, decimal quantity, string fromOrToQuantity, CancellationToken ct = default); /// /// Accept/confirm a convert estimate @@ -38,7 +38,7 @@ public interface IWhiteBitRestClientV4ApiConvert /// /// ["quoteId"] Quote/estimate id /// Cancellation token - Task> ConfirmConvertAsync(string estimateId, CancellationToken ct = default); + Task> ConfirmConvertAsync(string estimateId, CancellationToken ct = default); /// /// Get convert history @@ -57,7 +57,7 @@ public interface IWhiteBitRestClientV4ApiConvert /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetConvertHistoryAsync(string? fromAsset = null, string? toAsset = null, string? quoteId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetConvertHistoryAsync(string? fromAsset = null, string? toAsset = null, string? quoteId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiExchangeData.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiExchangeData.cs index 5df7f7c..ec77604 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiExchangeData.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiExchangeData.cs @@ -23,7 +23,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// Cancellation token /// - Task> GetServerTimeAsync(CancellationToken ct = default); + Task> GetServerTimeAsync(CancellationToken ct = default); /// /// Get symbols list @@ -35,7 +35,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetSymbolsAsync(CancellationToken ct = default); + Task> GetSymbolsAsync(CancellationToken ct = default); /// /// Get system/platform status @@ -47,7 +47,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetSystemStatusAsync(CancellationToken ct = default); + Task> GetSystemStatusAsync(CancellationToken ct = default); /// /// Get tickers @@ -59,7 +59,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetTickersAsync(CancellationToken ct = default); + Task> GetTickersAsync(CancellationToken ct = default); /// /// Get asset information @@ -71,7 +71,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetAssetsAsync(CancellationToken ct = default); + Task> GetAssetsAsync(CancellationToken ct = default); /// /// Get the order book for a symbol @@ -86,7 +86,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// ["limit"] The order book depth, max 100 /// ["level"] Aggregation level /// Cancellation token - Task> GetOrderBookAsync(string symbol, int? limit = null, int? mergeLevel = null, CancellationToken ct = default); + Task> GetOrderBookAsync(string symbol, int? limit = null, int? mergeLevel = null, CancellationToken ct = default); /// /// Get the most recent 100 trades @@ -100,7 +100,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// The symbol, for example `ETHUSDT` /// ["type"] Filter by trade side /// Cancellation token - Task> GetRecentTradesAsync(string symbol, OrderSide? side = null, CancellationToken ct = default); + Task> GetRecentTradesAsync(string symbol, OrderSide? side = null, CancellationToken ct = default); /// /// Get withdrawal/deposit limits and fees @@ -112,7 +112,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetDepositWithdrawalInfoAsync(CancellationToken ct = default); + Task> GetDepositWithdrawalInfoAsync(CancellationToken ct = default); /// /// Get collateral symbols @@ -124,7 +124,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetCollateralSymbolsAsync(CancellationToken ct = default); + Task> GetCollateralSymbolsAsync(CancellationToken ct = default); /// /// Get futures symbols @@ -136,7 +136,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// /// /// Cancellation token - Task> GetFuturesSymbolsAsync(CancellationToken ct = default); + Task> GetFuturesSymbolsAsync(CancellationToken ct = default); /// /// Get funding rate history @@ -147,7 +147,7 @@ public interface IWhiteBitRestClientV4ApiExchangeData /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetFundingHistoryAsync( + Task> GetFundingHistoryAsync( string symbol, DateTime? startTime = null, DateTime? endTime = null, diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiSubAccount.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiSubAccount.cs index 8e72cb4..e92e629 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiSubAccount.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiSubAccount.cs @@ -26,7 +26,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// ["permissions.spotEnabled"] Whether spot trading is enabled /// ["permissions.collateralEnabled"] Whether collateral trading is enabled /// Cancellation token - Task> CreateSubAccountAsync(string alias, string? email = null, bool? shareKyc = null, string? spotEnabled = null, string? collateralEnabled = null, CancellationToken ct = default); + Task> CreateSubAccountAsync(string alias, string? email = null, bool? shareKyc = null, string? spotEnabled = null, string? collateralEnabled = null, CancellationToken ct = default); /// /// Delete a sub account @@ -39,7 +39,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// /// ["id"] Sub account id /// Cancellation token - Task DeleteSubAccountAsync(string id, CancellationToken ct = default); + Task DeleteSubAccountAsync(string id, CancellationToken ct = default); /// /// Edit a sub account @@ -55,7 +55,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// ["permissions.spotEnabled"] Spot trading enabled /// ["permissions.collateralEnabled"] Collateral trading enabled /// Cancellation token - Task EditSubAccountAsync(string id, string alias, string spotEnabled, string collateralEnabled, CancellationToken ct = default); + Task EditSubAccountAsync(string id, string alias, string spotEnabled, string collateralEnabled, CancellationToken ct = default); /// /// Get sub account list @@ -70,7 +70,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetSubAccountsAsync(string? search = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetSubAccountsAsync(string? search = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Transfer to or from sub account @@ -86,7 +86,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// ["ticker"] The asset, for example `ETH` /// ["amount"] Quantity /// Cancellation token - Task SubaccountTransferAsync(string subaccountId, SubTransferDirection direction, string asset, decimal quantity, CancellationToken ct = default); + Task SubaccountTransferAsync(string subaccountId, SubTransferDirection direction, string asset, decimal quantity, CancellationToken ct = default); /// /// Block a sub account @@ -99,7 +99,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// /// ["id"] Sub account id /// Cancellation token - Task BlockSubaccountAsync(string id, CancellationToken ct = default); + Task BlockSubaccountAsync(string id, CancellationToken ct = default); /// /// Unblock a sub account @@ -112,7 +112,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// /// ["id"] Sub account id /// Cancellation token - Task UnblockSubaccountAsync(string id, CancellationToken ct = default); + Task UnblockSubaccountAsync(string id, CancellationToken ct = default); /// /// Get sub account balances @@ -126,7 +126,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// ["id"] Sub account id /// ["ticker"] Asset name /// Cancellation token - Task> GetSubaccountBalancesAsync(string id, string? asset = null, CancellationToken ct = default); + Task> GetSubaccountBalancesAsync(string id, string? asset = null, CancellationToken ct = default); /// /// Get sub account transfer history @@ -138,7 +138,7 @@ public interface IWhiteBitRestClientV4ApiSubAccount /// /// /// Cancellation token - Task> GetSubaccountTransferHistoryAsync(CancellationToken ct = default); + Task> GetSubaccountTransferHistoryAsync(CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiTrading.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiTrading.cs index bf28182..95e7d74 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiTrading.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitRestClientV4ApiTrading.cs @@ -44,7 +44,7 @@ public interface IWhiteBitRestClientV4ApiTrading /// ["stp"] Self trade prevention mode /// Cancellation token /// - Task> PlaceSpotOrderAsync( + Task> PlaceSpotOrderAsync( string symbol, OrderSide side, NewOrderType type, @@ -70,7 +70,7 @@ Task> PlaceSpotOrderAsync( /// /// ["orders"] Orders to place, max 20 /// Cancellation token - Task[]>> PlaceSpotMultipleOrdersAsync( + Task[]>> PlaceSpotMultipleOrdersAsync( IEnumerable requests, CancellationToken ct = default); @@ -87,7 +87,7 @@ Task[]>> PlaceSpotMultipleOrders /// ["orderId"] The order id, either this or `clientOrderId` has to be provided /// ["clientOrderId"] The client order id, either this or `id` should be provided /// Cancellation token - Task> CancelOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, CancellationToken ct = default); + Task> CancelOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, CancellationToken ct = default); /// /// Cancel multiple orders @@ -100,7 +100,7 @@ Task[]>> PlaceSpotMultipleOrders /// /// Order requests /// Cancellation token - Task> CancelOrdersAsync(IEnumerable requests, CancellationToken ct = default); + Task> CancelOrdersAsync(IEnumerable requests, CancellationToken ct = default); /// /// Cancel all orders @@ -114,7 +114,7 @@ Task[]>> PlaceSpotMultipleOrders /// ["market"] Filter by symbol /// ["type"] Filter by order product types /// Cancellation token - Task CancelAllOrdersAsync(string? symbol = null, IEnumerable? orderProductTypes = null, CancellationToken ct = default); + Task CancelAllOrdersAsync(string? symbol = null, IEnumerable? orderProductTypes = null, CancellationToken ct = default); /// /// Get list of currently open orders @@ -131,7 +131,7 @@ Task[]>> PlaceSpotMultipleOrders /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetOpenOrdersAsync(string? symbol = null, long? orderId = null, string? clientOrderId = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetOpenOrdersAsync(string? symbol = null, long? orderId = null, string? clientOrderId = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Get list of closed orders per symbol @@ -151,7 +151,7 @@ Task[]>> PlaceSpotMultipleOrders /// ["startDate"] Filter by start time /// ["endDate"] Filter by end time /// Cancellation token - Task>> GetClosedOrdersAsync( + Task>> GetClosedOrdersAsync( string? symbol = null, long? orderId = null, string? clientOrderId = null, @@ -178,7 +178,7 @@ Task>> GetClosedOrdersAs /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetUserTradesAsync(string? symbol = null, string? clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetUserTradesAsync(string? symbol = null, string? clientOrderId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Get trades for a specific order @@ -193,7 +193,7 @@ Task>> GetClosedOrdersAs /// ["limit"] Max number of results /// ["offset"] Result offset /// Cancellation token - Task> GetOrderTradesAsync(long orderId, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetOrderTradesAsync(long orderId, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Edit an order @@ -212,7 +212,7 @@ Task>> GetClosedOrdersAs /// ["price"] New price /// ["activationPrice"] New trigger price /// Cancellation token - Task> EditOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, decimal? quantity = null, decimal? quoteQuantity = null, decimal? price = null, decimal? triggerPrice = null, CancellationToken ct = default); + Task> EditOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, decimal? quantity = null, decimal? quoteQuantity = null, decimal? price = null, decimal? triggerPrice = null, CancellationToken ct = default); /// /// Set a kill switch. After the specified timeout all order fitting the parameters will be canceled unless the kill switch endpoint is called again to extend or cancel the timeout @@ -227,7 +227,7 @@ Task>> GetClosedOrdersAs /// ["timeout"] The timeout in seconds, or 0 to cancel the kill switch /// ["types"] Symbol product types /// Cancellation token - Task SetKillSwitchAsync(string symbol, int timeout, IEnumerable? symbolProductTypes = null, CancellationToken ct = default); + Task SetKillSwitchAsync(string symbol, int timeout, IEnumerable? symbolProductTypes = null, CancellationToken ct = default); /// /// Get the status of enabled kill switches @@ -240,6 +240,6 @@ Task>> GetClosedOrdersAs /// /// ["market"] Filter by symbol /// Cancellation token - Task> GetKillSwitchStatusAsync(string? symbol = null, CancellationToken ct = default); + Task> GetKillSwitchStatusAsync(string? symbol = null, CancellationToken ct = default); } } diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs index 894eab8..da417ca 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs @@ -24,7 +24,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results /// Filter by returning results later than the trade with this id /// Cancellation token - Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default); + Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default); /// /// Subscribe to public trade updates @@ -34,7 +34,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToTradeUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToTradeUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to public trade updates @@ -44,7 +44,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to public book ticker updates @@ -54,7 +54,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToBookTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToBookTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to public book ticker updates for all symbols @@ -63,7 +63,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToBookTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default); + Task> SubscribeToBookTickerUpdatesAsync(Action> onMessage, CancellationToken ct = default); /// /// Get the last price for a symbol @@ -71,7 +71,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// Symbol name /// Cancellation token - Task> GetLastPriceAsync(string symbol, CancellationToken ct = default); + Task> GetLastPriceAsync(string symbol, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -80,7 +80,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToLastPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToLastPriceUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -89,7 +89,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToLastPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToLastPriceUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); /// /// Get the ticker for a symbol @@ -97,7 +97,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// Symbol name /// Cancellation token - Task> GetTickerAsync(string symbol, CancellationToken ct = default); + Task> GetTickerAsync(string symbol, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -106,7 +106,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToTickerUpdatesAsync(string symbol, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -115,7 +115,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToTickerUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); /// /// Get klines for a symbol @@ -126,7 +126,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientFilter by start time /// Filter by end time /// Cancellation token - Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default); + Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default); /// /// Subscribe to order book updates @@ -137,7 +137,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToKlineUpdatesAsync(string symbol, KlineInterval interval, Action> onMessage, CancellationToken ct = default); /// /// Get the ticker for a symbol @@ -147,7 +147,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientOrder book depth, max 100 /// 0 - no interval default. Available values: "0.00000001", "0.0000001", "0.000001", "0.00001", "0.0001", "0.001", "0.01", "0.1" /// Cancellation token - Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default); + Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default); /// /// Subscribe to order book updates @@ -158,7 +158,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToOrderBookUpdatesAsync(string symbol, int depth, Action> onMessage, CancellationToken ct = default); /// /// Get spot balances @@ -166,7 +166,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// /// - Task> GetSpotBalancesAsync(CancellationToken ct = default); + Task> GetSpotBalancesAsync(CancellationToken ct = default); /// /// Subscribe to spot balance updates @@ -176,7 +176,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToSpotBalanceUpdatesAsync(IEnumerable assets, Action>> onMessage, CancellationToken ct = default); + Task> SubscribeToSpotBalanceUpdatesAsync(IEnumerable assets, Action>> onMessage, CancellationToken ct = default); /// /// Get margin balances @@ -184,7 +184,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// /// - Task> GetMarginBalancesAsync(CancellationToken ct = default); + Task> GetMarginBalancesAsync(CancellationToken ct = default); /// /// Subscribe to margin balance updates @@ -194,7 +194,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToMarginBalanceUpdatesAsync(IEnumerable assets, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToMarginBalanceUpdatesAsync(IEnumerable assets, Action> onMessage, CancellationToken ct = default); /// /// Get open orders @@ -204,7 +204,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to open order updates @@ -215,7 +215,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for handling an OTO order update /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToOpenOrderUpdatesAsync(IEnumerable symbols, Action> onOrderMessage, Action>? onOtoOrdersMessage = null, CancellationToken ct = default); + Task> SubscribeToOpenOrderUpdatesAsync(IEnumerable symbols, Action> onOrderMessage, Action>? onOtoOrdersMessage = null, CancellationToken ct = default); /// /// Get closed orders @@ -226,7 +226,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to closed order updates @@ -237,7 +237,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToClosedOrderUpdatesAsync(IEnumerable symbols, ClosedOrderFilter filter, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToClosedOrderUpdatesAsync(IEnumerable symbols, ClosedOrderFilter filter, Action> onMessage, CancellationToken ct = default); /// /// Get user trades @@ -247,7 +247,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to user trade updates @@ -257,7 +257,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToUserTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); + Task> SubscribeToUserTradeUpdatesAsync(IEnumerable symbols, Action> onMessage, CancellationToken ct = default); /// /// Subscribe to position updates @@ -266,7 +266,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default); + Task> SubscribeToPositionUpdatesAsync(Action> onMessage, CancellationToken ct = default); /// /// Subscribe to borrow updates @@ -275,7 +275,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToBorrowUpdatesAsync(Action> onMessage, CancellationToken ct = default); + Task> SubscribeToBorrowUpdatesAsync(Action> onMessage, CancellationToken ct = default); /// /// Subscribe to user margin call and liquidation events @@ -284,7 +284,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToAccountBorrowEventUpdatesAsync(Action> onMessage, CancellationToken ct = default); + Task> SubscribeToAccountBorrowEventUpdatesAsync(Action> onMessage, CancellationToken ct = default); /// /// Subscribe to position margin call and liquidation events @@ -293,7 +293,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientThe event handler for the received data /// Cancellation token for closing this subscription /// A stream subscription. This stream subscription can be used to be notified when the socket is disconnected/reconnected - Task> SubscribeToAccountMarginPositionEventUpdatesAsync(Action> onMessage, CancellationToken ct = default); + Task> SubscribeToAccountMarginPositionEventUpdatesAsync(Action> onMessage, CancellationToken ct = default); /// /// Get the shared socket requests client. This interface is shared with other exchanges to allow for a common implementation for different exchanges. diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitBookSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitBookSubscription.cs index f622df2..2314066 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitBookSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitBookSubscription.cs @@ -32,7 +32,7 @@ public WhiteBitBookSubscription(ILogger logger, SocketApiClient client, string s _depth = depth; Topic = "OrderBook"; - MessageRouter = MessageRouter.CreateWithTopicFilter>("depth_update", symbol, DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent>("depth_update", symbol, DoHandleMessage); } /// @@ -70,7 +70,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithUpdateType(message.Data!.Snapshot ? SocketUpdateType.Snapshot : SocketUpdateType.Update) .WithSequenceNumber(message.Data.OrderBook.UpdateId) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitClosedOrderSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitClosedOrderSubscription.cs index b6f0604..77460c0 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitClosedOrderSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitClosedOrderSubscription.cs @@ -32,7 +32,7 @@ public WhiteBitClosedOrderSubscription(ILogger logger, SocketApiClient client, I _handler = handler; _symbols = symbols.ToArray(); _orderFilter = orderFilter; - MessageRouter = MessageRouter.CreateWithoutTopicFilter>("depth_update", DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent>("depth_update", DoHandleMessage); Topic = "ClosedOrder"; } @@ -68,7 +68,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitKlineSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitKlineSubscription.cs index 5df06d0..e825700 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitKlineSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitKlineSubscription.cs @@ -34,7 +34,7 @@ public WhiteBitKlineSubscription(ILogger logger, SocketApiClient client, string _interval = interval; Topic = "Klines"; - MessageRouter = MessageRouter.CreateWithTopicFilter>("candles_update", symbol, DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent>("candles_update", symbol, DoHandleMessage); } /// @@ -69,7 +69,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitMarginBalanceSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitMarginBalanceSubscription.cs index 9d237cc..384ba7f 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitMarginBalanceSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitMarginBalanceSubscription.cs @@ -32,7 +32,7 @@ public WhiteBitMarginBalanceSubscription(ILogger logger, SocketApiClient client, _symbols = symbols.ToArray(); Topic = "MarginBalance"; - MessageRouter = MessageRouter.CreateWithoutTopicFilter>("balanceMargin_update", DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent>("balanceMargin_update", DoHandleMessage); } /// @@ -65,7 +65,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitOpenOrderSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitOpenOrderSubscription.cs index fda2373..3c8efe0 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitOpenOrderSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitOpenOrderSubscription.cs @@ -39,8 +39,8 @@ public WhiteBitOpenOrderSubscription(ILogger logger, SocketApiClient client, IEn var routes = new List(); foreach (var symbol in symbols) { - routes.Add(MessageRoute>.CreateWithTopicFilter("ordersPending_update", symbol, DoHandleMessage)); - routes.Add(MessageRoute>.CreateWithTopicFilter("otoOrdersPending_update", symbol, DoHandleMessage)); + routes.Add(EventRoute>.CreateWithTopicFilter("ordersPending_update", symbol, DoHandleMessage)); + routes.Add(EventRoute>.CreateWithTopicFilter("otoOrdersPending_update", symbol, DoHandleMessage)); } MessageRouter = MessageRouter.Create(routes.ToArray()); @@ -77,7 +77,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithSymbol(message.Data!.Order.TriggerOrder?.Symbol) .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } /// @@ -89,7 +89,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithSymbol(message.Data!.Order.Symbol) .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSpotBalanceSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSpotBalanceSubscription.cs index 3e2f5ad..0c635f7 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSpotBalanceSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSpotBalanceSubscription.cs @@ -32,7 +32,7 @@ public WhiteBitSpotBalanceSubscription(ILogger logger, SocketApiClient client, I _symbols = symbols.ToArray(); Topic = "SpotBalance"; - MessageRouter = MessageRouter.CreateWithoutTopicFilter[]>>("balanceSpot_update", DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent[]>>("balanceSpot_update", DoHandleMessage); } /// @@ -67,7 +67,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT _handler.Invoke( new DataEvent>(WhiteBitExchange.ExchangeName, balances, receiveTime, originalData) .WithUpdateType(SocketUpdateType.Update)); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSubscription.cs index 42ddc00..ae5e35d 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitSubscription.cs @@ -31,9 +31,11 @@ public WhiteBitSubscription(ILogger logger, SocketApiClient client, string topic _symbols = symbols; Topic = topic; - IndividualSubscriptionCount = symbols?.Length ?? 1; + IndividualSubscriptionCount = symbols?.Length == 0 ? 1 : symbols?.Length ?? 1; - MessageRouter = MessageRouter.CreateWithOptionalTopicFilters>($"{topic}_update", symbols, DoHandleMessage); + MessageRouter = symbols?.Length > 0 + ? MessageRouter.CreateForEvent>($"{topic}_update", symbols, DoHandleMessage) + : MessageRouter.CreateForEvent>($"{topic}_update", DoHandleMessage); } /// @@ -62,7 +64,7 @@ public WhiteBitSubscription(ILogger logger, SocketApiClient client, string topic public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveTime, string? originalData, WhiteBitSocketUpdate message) { _handler.Invoke(receiveTime, originalData, ConnectionInvocations, message); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitUserTradeSubscription.cs b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitUserTradeSubscription.cs index 726aba2..d2620df 100644 --- a/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitUserTradeSubscription.cs +++ b/WhiteBit.Net/Objects/Sockets/Subscriptions/WhiteBitUserTradeSubscription.cs @@ -35,7 +35,7 @@ public WhiteBitUserTradeSubscription(ILogger logger, SocketApiClient client, str IndividualSubscriptionCount = symbols.Length; Topic = "UserTrade"; - MessageRouter = MessageRouter.CreateWithoutTopicFilter>("deals_update", DoHandleMessage); + MessageRouter = MessageRouter.CreateForEvent>("deals_update", DoHandleMessage); } /// @@ -72,7 +72,7 @@ public CallResult DoHandleMessage(SocketConnection connection, DateTime receiveT .WithDataTimestamp(message.Data.Time, _client.GetTimeOffset()) .WithUpdateType(SocketUpdateType.Update) ); - return CallResult.SuccessResult; + return CallResult.Ok(); } } } diff --git a/WhiteBit.Net/Objects/Sockets/WhiteBitQuery.cs b/WhiteBit.Net/Objects/Sockets/WhiteBitQuery.cs index 0db6f23..7ab81a3 100644 --- a/WhiteBit.Net/Objects/Sockets/WhiteBitQuery.cs +++ b/WhiteBit.Net/Objects/Sockets/WhiteBitQuery.cs @@ -16,15 +16,15 @@ public WhiteBitQuery(SocketApiClient client, WhiteBitSocketRequest request, bool { _client = client; - MessageRouter = MessageRouter.CreateWithoutTopicFilter>(request.Id.ToString(), HandleMessage); + MessageRouter = MessageRouter.CreateForQuery>(request.Id.ToString(), HandleMessage); } public CallResult> HandleMessage(SocketConnection connection, DateTime receiveTime, string? originalData, WhiteBitSocketResponse message) { if (message.Error != null) - return new CallResult>(new ServerError(message.Error.Code, _client.GetErrorInfo(message.Error.Code, message.Error.Message))); + return CallResult.Fail>(new ServerError(message.Error.Code, _client.GetErrorInfo(message.Error.Code, message.Error.Message))); - return new CallResult>(message, originalData, null); + return CallResult>.Ok(message, originalData); } } } diff --git a/WhiteBit.Net/SymbolOrderBooks/WhiteBitV4SymbolOrderBook.cs b/WhiteBit.Net/SymbolOrderBooks/WhiteBitV4SymbolOrderBook.cs index d19f4a7..c65c62a 100644 --- a/WhiteBit.Net/SymbolOrderBooks/WhiteBitV4SymbolOrderBook.cs +++ b/WhiteBit.Net/SymbolOrderBooks/WhiteBitV4SymbolOrderBook.cs @@ -68,22 +68,22 @@ public WhiteBitV4SymbolOrderBook( protected override async Task> DoStartAsync(CancellationToken ct) { var result = await _socketClient.V4Api.SubscribeToOrderBookUpdatesAsync(Symbol, Levels!.Value, ProcessUpdate).ConfigureAwait(false); - if (!result) - return result; + if (!result.Success) + return CallResult.Fail(result.Error!); if (ct.IsCancellationRequested) { await result.Data.CloseAsync().ConfigureAwait(false); - return result.AsError(new CancellationRequestedError()); + return CallResult.Fail(new CancellationRequestedError()); } Status = OrderBookStatus.Syncing; var setResult = await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); - if (!setResult) + if (!setResult.Success) await result.Data.CloseAsync().ConfigureAwait(false); - return setResult ? result : new CallResult(setResult.Error!); + return setResult.Success ? CallResult.Ok(result.Data) : CallResult.Fail(setResult.Error!); } @@ -106,7 +106,7 @@ protected override void DoReset() } /// - protected override async Task> DoResyncAsync(CancellationToken ct) + protected override async Task DoResyncAsync(CancellationToken ct) { return await WaitForSetOrderBookAsync(_initialDataTimeout, ct).ConfigureAwait(false); } diff --git a/WhiteBit.Net/WhiteBit.Net.csproj b/WhiteBit.Net/WhiteBit.Net.csproj index 2903e4e..31eacc2 100644 --- a/WhiteBit.Net/WhiteBit.Net.csproj +++ b/WhiteBit.Net/WhiteBit.Net.csproj @@ -53,10 +53,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file + + + + diff --git a/WhiteBit.Net/WhiteBitAuthenticationProvider.cs b/WhiteBit.Net/WhiteBitAuthenticationProvider.cs index 8e7c01c..7321fd2 100644 --- a/WhiteBit.Net/WhiteBitAuthenticationProvider.cs +++ b/WhiteBit.Net/WhiteBitAuthenticationProvider.cs @@ -28,7 +28,7 @@ public override void ProcessRequest(RestApiClient apiClient, RestRequestConfigur return; var nonce = _nonceProvider.GetNonce().ToString(); - request.BodyParameters ??= new Dictionary(); + request.BodyParameters ??= new Parameters(WhiteBitExchange._parameterSerializationSettings); request.BodyParameters.Add("request", request.Path); request.BodyParameters.Add("nonce", nonce); request.BodyParameters.Add("nonceWindow", ((WhiteBitRestOptions)apiClient.ClientOptions).EnableNonceWindow); diff --git a/WhiteBit.Net/WhiteBitExchange.cs b/WhiteBit.Net/WhiteBitExchange.cs index 223ce91..a3b6cbc 100644 --- a/WhiteBit.Net/WhiteBitExchange.cs +++ b/WhiteBit.Net/WhiteBitExchange.cs @@ -63,6 +63,11 @@ public static class WhiteBitExchange public static ExchangeType Type { get; } = ExchangeType.CEX; internal static JsonSerializerOptions _serializerContext = SerializerOptions.WithConverters(JsonSerializerContextCache.GetOrCreate(), new ClosedOrdersConverter()); + internal static ParameterSerializationSettings _parameterSerializationSettings = new ParameterSerializationSettings + { + Decimal = DecimalSerialization.String, + Sort = false + }; /// /// Aliases for WhiteBit assets From 378ba21c7e77ee67ecc05bc7987cc5516fcf9fb8 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Wed, 10 Jun 2026 09:16:06 +0200 Subject: [PATCH 02/15] wip --- .../Clients/V4Api/WhiteBitRestClientV4Api.cs | 30 ++-------- .../V4Api/WhiteBitRestClientV4ApiAccount.cs | 48 ++++++++------- .../V4Api/WhiteBitRestClientV4ApiCodes.cs | 8 +-- ...hiteBitRestClientV4ApiCollateralTrading.cs | 16 ++--- .../V4Api/WhiteBitRestClientV4ApiConvert.cs | 6 +- .../WhiteBitRestClientV4ApiExchangeData.cs | 34 ++++++----- .../V4Api/WhiteBitRestClientV4ApiShared.cs | 59 ++++++++++--------- .../WhiteBitRestClientV4ApiSubAccount.cs | 18 +++--- .../V4Api/WhiteBitRestClientV4ApiTrading.cs | 33 ++++++----- .../V4Api/WhiteBitSocketClientV4Api.cs | 15 +++-- .../V4Api/WhiteBitSocketClientV4ApiShared.cs | 26 ++++---- .../WhiteBitAuthenticationProvider.cs | 4 +- 12 files changed, 147 insertions(+), 150 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs index 00dc79c..f281dd6 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs @@ -73,38 +73,18 @@ internal WhiteBitRestClientV4Api(ILogger logger, HttpClient? httpClient, WhiteBi protected override WhiteBitAuthenticationProvider CreateAuthenticationProvider(WhiteBitCredentials credentials) => new WhiteBitAuthenticationProvider(credentials, ClientOptions.NonceProvider ?? new WhiteBitNonceProvider()); - internal Task SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) - => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); - - internal async Task SendToAddressAsync(string baseAddress, RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) + internal async Task SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) { - var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); + var result = await base.SendAsync(definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); if (!result.Success && result.Error is DeserializeError) - return new HttpResult(result.Exchange, result.Data, null) - { - ResponseStatusCode = result.ResponseStatusCode, - HttpVersion = result.HttpVersion, - ResponseHeaders = result.ResponseHeaders, - ResponseTime = result.ResponseTime, - ResponseLength = result.ResponseLength, - OriginalData = result.OriginalData, - RequestId = result.RequestId, - RequestUrl = result.RequestUrl, - RequestBody = result.RequestBody, - RequestMethod = result.RequestMethod, - RequestHeaders = result.RequestHeaders, - DataSource = result.DataSource, - }; + return HttpResult.Ok(result); // Deserialize error without data expected is not an issue return result; } - internal Task> SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class - => SendToAddressAsync(BaseAddress, definition, parameters, cancellationToken, weight); - - internal async Task> SendToAddressAsync(string baseAddress, RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class + internal async Task> SendAsync(RequestDefinition definition, Parameters? parameters, CancellationToken cancellationToken, int? weight = null) where T : class { - var result = await base.SendAsync(baseAddress, definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); + var result = await base.SendAsync(definition, parameters, cancellationToken, null, weight).ConfigureAwait(false); return result; } diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs index 275e2d0..6598448 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs @@ -29,7 +29,7 @@ internal WhiteBitRestClientV4ApiAccount(WhiteBitRestClientV4Api baseClient) public async Task> GetMainBalancesAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -38,7 +38,7 @@ public async Task> GetMainBalancesAsync(Cancel foreach (var item in result.Data) item.Value.Asset = item.Key; - return HttpResult.Ok(result, result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data.Values.ToArray()); } #endregion @@ -51,7 +51,7 @@ public async Task> GetDepositAddressAsync var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); parameters.Add("network", network); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -66,7 +66,7 @@ public async Task> GetSpotBalancesAsync(strin { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/trade-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -75,7 +75,7 @@ public async Task> GetSpotBalancesAsync(strin foreach (var item in result.Data) item.Value.Asset = item.Key; - return HttpResult.Ok(result, result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data.Values.ToArray()); } #endregion @@ -108,7 +108,7 @@ public async Task> GetFiatDepositAddressAsync(str if (customer.Any()) parameters.Add("customer", customer); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/fiat-deposit-url", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/fiat-deposit-url", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -143,7 +143,7 @@ public async Task WithdrawAsync(string asset, decimal quantity, stri if (!deductFeeFromOutput) path = "/api/v4/main-account/withdraw-pay"; - var request = _definitions.GetOrCreate(HttpMethod.Post, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -161,7 +161,7 @@ public async Task TransferAsync(AccountType fromAccount, AccountType parameters.Add("to", toAccount); parameters.Add("ticker", asset); parameters.Add("amount", quantity); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -183,7 +183,7 @@ public async Task> GetDepositWithdrawalHi parameters.Add("uniqueId", uniqueId); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(200, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -200,7 +200,7 @@ public async Task> CreateDepositAddressAs parameters.Add("ticker", asset); parameters.Add("network", network); parameters.Add("type", addressType); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/create-new-address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/create-new-address", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -214,7 +214,7 @@ public async Task> CreateDepositAddressAs public async Task> GetDepositWithdrawalSettingsAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -233,7 +233,7 @@ public async Task> GetMiningRewardHistoryAsync parameters.Add("to", endTime); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/mining/rewards", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/mining/rewards", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -248,7 +248,7 @@ public async Task>> GetCollateralBalances { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("ticker", asset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/balance", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); return result; @@ -262,7 +262,7 @@ public async Task>> GetCollateralBalances public async Task> GetCollateralBalanceSummaryAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/balance-summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/balance-summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; } @@ -275,7 +275,7 @@ public async Task> GetCollateralBalanceS public async Task> GetCollateralAccountSummaryAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/summary", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -304,6 +304,7 @@ public async Task> GetAccountFunding parameters.Add("limit", limit); parameters.Add("offset", offset); var request = _definitions.GetOrCreate(HttpMethod.Post, + _baseClient.BaseAddress, "/api/v4/collateral-account/funding-history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); @@ -318,7 +319,7 @@ public async Task> SetAccountLeverageAsync(int leve { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("leverage", leverage); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/leverage", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/leverage", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -334,7 +335,7 @@ public async Task> SetAccountLeverageAsync(int leve //{ // var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); // parameters.Add("market", symbol); - // var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + // var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); // return await _baseClient.SendAsync(request, parameters, null, ct).ConfigureAwait(false); //} @@ -345,7 +346,7 @@ public async Task> SetAccountLeverageAsync(int leve /// public async Task> GetTradingFeesAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/market/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); return await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); } @@ -357,7 +358,7 @@ public async Task> GetTradingFeesAsync(Cancellat public async Task> GetHedgeModeAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/hedge-mode", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/hedge-mode", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; } @@ -371,7 +372,7 @@ public async Task> SetHedgeModeAsync(bool enableHe { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("hedgeMode", enableHedgeMode); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/hedge-mode/update", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/hedge-mode/update", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; } @@ -381,9 +382,12 @@ public async Task> SetHedgeModeAsync(bool enableHe internal async Task> GetWebsocketTokenAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/profile/websocket_token", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/profile/websocket_token", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); - return HttpResult.Ok(result, result.Data?.Token); + if (!result.Success) + return HttpResult.Fail(result); + + return HttpResult.Ok(result, result.Data.Token); } } } diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs index 275f3dc..e389477 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCodes.cs @@ -30,7 +30,7 @@ public async Task> CreateCodeAsync(string asset, decima parameters.Add("amount", quantity); parameters.Add("passphrase", passphrase); parameters.Add("description", description); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/codes", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -46,7 +46,7 @@ public async Task> ApplyCodeAsync(string code, st var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("code", code); parameters.Add("passphrase", passphrase); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes/apply", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/codes/apply", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(60, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -62,7 +62,7 @@ public async Task> GetCreatedCodesAsync(int? var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/main-account/codes/my", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/main-account/codes/my", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -78,7 +78,7 @@ public async Task> GetCodeHistoryAsync(int? l var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/main-account/codes/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/main-account/codes/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs index 0df5567..82d93a0 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiCollateralTrading.cs @@ -74,7 +74,7 @@ public async Task> PlaceOrderAsync( else throw new ArgumentException("Unknown path for order type"); - var request = _definitions.GetOrCreate(HttpMethod.Post, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -89,7 +89,7 @@ public async Task> GetOpenPositionsAsync(string? { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/positions/open", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/positions/open", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -109,7 +109,7 @@ public async Task> GetPositionHistoryAsync parameters.Add("endDate", endTime); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/collateral-account/positions/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/collateral-account/positions/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -126,7 +126,7 @@ public async Task> GetOpenConditiona parameters.Add("market", symbol); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/conditional-orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/conditional-orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -159,7 +159,7 @@ public async Task> PlaceOcoOrderAsync( parameters.Add("clientOrderId", clientOrderId); parameters.Add("positionSide", positionSide); parameters.Add("reduceOnly", reduceOnly); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/collateral/oco", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/collateral/oco", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; } @@ -174,7 +174,7 @@ public async Task> CancelOcoOrderAsync(string symbo var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("orderId", orderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/oco-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/oco-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -190,7 +190,7 @@ public async Task CancelConditionalOrderAsync(string symbol, long or var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("id", orderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/conditional-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/conditional-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -206,7 +206,7 @@ public async Task CancelOTOOrderAsync(string symbol, long orderId, C var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); parameters.Add("otoId", orderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/oto-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/oto-cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs index f196080..20b9170 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs @@ -30,7 +30,7 @@ public async Task> GetConvertEstimateAsync(s parameters.Add("to", toAsset); parameters.Add("amount", quantity); parameters.Add("direction", fromOrToQuantity); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/estimate", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/convert/estimate", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -45,7 +45,7 @@ public async Task> ConfirmConvertAsync(string { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("quoteId", estimateId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/confirm", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/convert/confirm", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -66,7 +66,7 @@ public async Task> GetConvertHistoryAsync(str parameters.Add("to", endTime); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/convert/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs index 8681eb3..27184ec 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiExchangeData.cs @@ -30,7 +30,7 @@ internal WhiteBitRestClientV4ApiExchangeData(ILogger logger, WhiteBitRestClientV /// public async Task> GetServerTimeAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/time", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/time", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); return HttpResult.Ok(result, result.Data?.Timestamp ?? default); @@ -44,7 +44,7 @@ public async Task> GetServerTimeAsync(CancellationToken ct public async Task> GetSymbolsAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -58,7 +58,7 @@ public async Task> GetSymbolsAsync(CancellationToke public async Task> GetSystemStatusAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/platform/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/platform/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -72,7 +72,7 @@ public async Task> GetSystemStatusAsync(Cancell public async Task> GetTickersAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/ticker", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/ticker", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -92,7 +92,7 @@ public async Task> GetTickersAsync(CancellationToke public async Task> GetAssetsAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/assets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/assets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -101,7 +101,7 @@ public async Task> GetAssetsAsync(CancellationToken foreach (var item in result.Data) item.Value.Asset = item.Key; - return HttpResult.Ok(result, result.Data?.Values.ToArray()); + return HttpResult.Ok(result, result.Data.Values.ToArray()); } #endregion @@ -114,7 +114,7 @@ public async Task> GetOrderBookAsync(string symbol var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("limit", limit); parameters.Add("level", mergeLevel); - var request = _definitions.GetOrCreate(HttpMethod.Get, $"/api/v4/public/orderbook/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, $"/api/v4/public/orderbook/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(600, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -129,7 +129,7 @@ public async Task> GetRecentTradesAsync(string symbo { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("type", side); - var request = _definitions.GetOrCreate(HttpMethod.Get, $"/api/v4/public/trades/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, $"/api/v4/public/trades/{symbol}", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -143,7 +143,7 @@ public async Task> GetRecentTradesAsync(string symbo public async Task> GetDepositWithdrawalInfoAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/fee", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -157,10 +157,13 @@ public async Task> GetDepositWithdrawalInfoA public async Task> GetCollateralSymbolsAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/collateral/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/collateral/markets", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return HttpResult.Ok(result, result.Data?.Result); + if (!result.Success) + return HttpResult.Fail(result); + + return HttpResult.Ok(result, result.Data.Result!); } #endregion @@ -171,10 +174,13 @@ public async Task> GetCollateralSymbolsAsync(CancellationTo public async Task> GetFuturesSymbolsAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/futures", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/futures", WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return HttpResult.Ok(result, result.Data?.Result); + if (!result.Success) + return HttpResult.Fail(result); + + return HttpResult.Ok(result, result.Data.Result!); } #endregion @@ -195,7 +201,7 @@ public async Task> GetFundingHistoryAsync( parameters.Add("endDate", endTime); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/api/v4/public/funding-history/" + symbol, WhiteBitExchange.RateLimiter.WhiteBit, 1, false, + var request = _definitions.GetOrCreate(HttpMethod.Get, _baseClient.BaseAddress, "/api/v4/public/funding-history/" + symbol, WhiteBitExchange.RateLimiter.WhiteBit, 1, false, limitGuard: new SingleLimitGuard(2000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs index f159f8e..432dd73 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs @@ -20,16 +20,15 @@ internal partial class WhiteBitRestClientV4Api : IWhiteBitRestClientV4ApiShared private const string _topicSpotId = "WhiteBitSpot"; private const string _topicFuturesId = "WhiteBitFutures"; - public string Exchange => _exchange; - public TradingMode[] SupportedTradingModes => new[] { TradingMode.Spot, TradingMode.PerpetualLinear }; public TradingMode[] SupportedFuturesModes => new[] { TradingMode.PerpetualLinear }; public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); + public SharedClientInfo Discover() => SharedUtils.GetClientInfo(this); #region Spot Symbol client - EndpointOptions ISpotSymbolRestClient.GetSpotSymbolsOptions { get; } = new EndpointOptions(_exchange, false); + GetSpotSymbolsOptions ISpotSymbolRestClient.GetSpotSymbolsOptions { get; } = new GetSpotSymbolsOptions(_exchange, false); async Task> ISpotSymbolRestClient.GetSpotSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) { @@ -51,7 +50,7 @@ async Task> ISpotSymbolRestClient.GetSpotSymbolsA PriceDecimals = s.QuoteAssetPrecision }).ToArray()); - ExchangeSymbolCache.UpdateSymbolInfo(_topicSpotId, response.Data); + ExchangeSymbolCache.UpdateSymbolInfo(_topicSpotId, response.Data!); return response; } @@ -140,7 +139,7 @@ async Task> ISpotTickerRestClient.GetSpotTickersA #region Book Ticker client - EndpointOptions IBookTickerRestClient.GetBookTickerOptions { get; } = new EndpointOptions(_exchange, false); + GetBookTickerOptions IBookTickerRestClient.GetBookTickerOptions { get; } = new GetBookTickerOptions(_exchange, false); async Task> IBookTickerRestClient.GetBookTickerAsync(GetBookTickerRequest request, CancellationToken ct) { var validationError = SharedClient.GetBookTickerOptions.ValidateRequest(request, this); @@ -250,7 +249,7 @@ async Task> IBalanceRestClient.GetBalancesAsync(GetB #endregion #region Asset client - EndpointOptions IAssetsRestClient.GetAssetsOptions { get; } = new EndpointOptions(_exchange, true); + GetAssetsOptions IAssetsRestClient.GetAssetsOptions { get; } = new GetAssetsOptions(_exchange, true); async Task> IAssetsRestClient.GetAssetsAsync(GetAssetsRequest request, CancellationToken ct) { @@ -277,7 +276,7 @@ async Task> IAssetsRestClient.GetAssetsAsync(GetAssets }).ToArray()); } - EndpointOptions IAssetsRestClient.GetAssetOptions { get; } = new EndpointOptions(_exchange, false); + GetAssetOptions IAssetsRestClient.GetAssetOptions { get; } = new GetAssetOptions(_exchange, false); async Task> IAssetsRestClient.GetAssetAsync(GetAssetRequest request, CancellationToken ct) { var validationError = SharedClient.GetAssetOptions.ValidateRequest(request, this); @@ -308,7 +307,7 @@ async Task> IAssetsRestClient.GetAssetAsync(GetAssetRequ #region Deposit client - EndpointOptions IDepositRestClient.GetDepositAddressesOptions { get; } = new EndpointOptions(_exchange, true); + GetDepositAddressesOptions IDepositRestClient.GetDepositAddressesOptions { get; } = new GetDepositAddressesOptions(_exchange, true); async Task> IDepositRestClient.GetDepositAddressesAsync(GetDepositAddressesRequest request, CancellationToken ct) { var validationError = SharedClient.GetDepositAddressesOptions.ValidateRequest(request, this); @@ -385,7 +384,9 @@ private SharedTransferStatus ParseTransferStatus(TransactionStatus? transactionS || transactionStatus == TransactionStatus.ConfirmationInProgress || transactionStatus == TransactionStatus.Pending || transactionStatus == TransactionStatus.Uncredited) + { return SharedTransferStatus.InProgress; + } return SharedTransferStatus.Unknown; } @@ -506,7 +507,7 @@ async Task> ISpotOrderRestClient.PlaceSpotOrderAsync(PlaceS return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions ISpotOrderRestClient.GetSpotOrderOptions { get; } = new EndpointOptions(_exchange, true); + GetSpotOrderOptions ISpotOrderRestClient.GetSpotOrderOptions { get; } = new GetSpotOrderOptions(_exchange, true); async Task> ISpotOrderRestClient.GetSpotOrderAsync(GetOrderRequest request, CancellationToken ct) { var validationError = SharedClient.GetSpotOrderOptions.ValidateRequest(request, this); @@ -579,7 +580,7 @@ async Task> ISpotOrderRestClient.GetSpotOrderAsync(G } } - EndpointOptions ISpotOrderRestClient.GetOpenSpotOrdersOptions { get; } = new EndpointOptions(_exchange, true); + GetOpenSpotOrdersOptions ISpotOrderRestClient.GetOpenSpotOrdersOptions { get; } = new GetOpenSpotOrdersOptions(_exchange, true); async Task> ISpotOrderRestClient.GetOpenSpotOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) { var validationError = SharedClient.GetOpenSpotOrdersOptions.ValidateRequest(request, this); @@ -674,7 +675,7 @@ async Task> ISpotOrderRestClient.GetClosedSpotOrde return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), nextPageRequest); } - EndpointOptions ISpotOrderRestClient.GetSpotOrderTradesOptions { get; } = new EndpointOptions(_exchange, true); + GetSpotOrderTradesOptions ISpotOrderRestClient.GetSpotOrderTradesOptions { get; } = new GetSpotOrderTradesOptions(_exchange, true); async Task> ISpotOrderRestClient.GetSpotOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) { var validationError = SharedClient.GetSpotOrderTradesOptions.ValidateRequest(request, this); @@ -757,7 +758,7 @@ async Task> ISpotOrderRestClient.GetSpotUserTrades }).ToArray(), nextPageRequest); } - EndpointOptions ISpotOrderRestClient.CancelSpotOrderOptions { get; } = new EndpointOptions(_exchange, true); + CancelSpotOrderOptions ISpotOrderRestClient.CancelSpotOrderOptions { get; } = new CancelSpotOrderOptions(_exchange, true); async Task> ISpotOrderRestClient.CancelSpotOrderAsync(CancelOrderRequest request, CancellationToken ct) { var validationError = SharedClient.CancelSpotOrderOptions.ValidateRequest(request, this); @@ -796,7 +797,7 @@ private SharedOrderType ParseOrderType(OrderType type, bool postOnly) #region Futures Symbol client - EndpointOptions IFuturesSymbolRestClient.GetFuturesSymbolsOptions { get; } = new EndpointOptions(_exchange, false); + GetFuturesSymbolsOptions IFuturesSymbolRestClient.GetFuturesSymbolsOptions { get; } = new GetFuturesSymbolsOptions(_exchange, false); async Task> IFuturesSymbolRestClient.GetFuturesSymbolsAsync(GetSymbolsRequest request, CancellationToken ct) { var validationError = SharedClient.GetFuturesSymbolsOptions.ValidateRequest(request, this); @@ -827,7 +828,7 @@ async Task> IFuturesSymbolRestClient.GetFuture }; }).ToArray()); - ExchangeSymbolCache.UpdateSymbolInfo(_topicFuturesId, response.Data); + ExchangeSymbolCache.UpdateSymbolInfo(_topicFuturesId, response.Data!); return response; } @@ -925,7 +926,7 @@ async Task> IFuturesTickerRestClient.GetFuture #region Leverage client SharedLeverageSettingMode ILeverageRestClient.LeverageSettingType => SharedLeverageSettingMode.PerAccount; - EndpointOptions ILeverageRestClient.GetLeverageOptions { get; } = new EndpointOptions(_exchange, true); + GetLeverageOptions ILeverageRestClient.GetLeverageOptions { get; } = new GetLeverageOptions(_exchange, true); async Task> ILeverageRestClient.GetLeverageAsync(GetLeverageRequest request, CancellationToken ct) { var validationError = SharedClient.GetLeverageOptions.ValidateRequest(request, this); @@ -956,7 +957,7 @@ async Task> ILeverageRestClient.SetLeverageAsync(SetL #region Open Interest client - EndpointOptions IOpenInterestRestClient.GetOpenInterestOptions { get; } = new EndpointOptions(_exchange, true); + GetOpenInterestOptions IOpenInterestRestClient.GetOpenInterestOptions { get; } = new GetOpenInterestOptions(_exchange, true); async Task> IOpenInterestRestClient.GetOpenInterestAsync(GetOpenInterestRequest request, CancellationToken ct) { var validationError = SharedClient.GetOpenInterestOptions.ValidateRequest(request, this); @@ -1076,7 +1077,7 @@ async Task> IFuturesOrderRestClient.PlaceFuturesOrderAsync( return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions IFuturesOrderRestClient.GetFuturesOrderOptions { get; } = new EndpointOptions(_exchange, true); + GetFuturesOrderOptions IFuturesOrderRestClient.GetFuturesOrderOptions { get; } = new GetFuturesOrderOptions(_exchange, true); async Task> IFuturesOrderRestClient.GetFuturesOrderAsync(GetOrderRequest request, CancellationToken ct) { var validationError = SharedClient.GetFuturesOrderOptions.ValidateRequest(request, this); @@ -1157,7 +1158,7 @@ async Task> IFuturesOrderRestClient.GetFuturesOrd } } - EndpointOptions IFuturesOrderRestClient.GetOpenFuturesOrdersOptions { get; } = new EndpointOptions(_exchange, true); + GetOpenFuturesOrdersOptions IFuturesOrderRestClient.GetOpenFuturesOrdersOptions { get; } = new GetOpenFuturesOrdersOptions(_exchange, true); async Task> IFuturesOrderRestClient.GetOpenFuturesOrdersAsync(GetOpenOrdersRequest request, CancellationToken ct) { var validationError = SharedClient.GetOpenFuturesOrdersOptions.ValidateRequest(request, this); @@ -1260,7 +1261,7 @@ x.Status is OrderStatus.Canceled or OrderStatus.AutoCanceledUserMargin return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(data, x => x.CreateTime!.Value, request.StartTime, request.EndTime, direction).ToArray(), nextPageRequest); } - EndpointOptions IFuturesOrderRestClient.GetFuturesOrderTradesOptions { get; } = new EndpointOptions(_exchange, true); + GetFuturesOrderTradesOptions IFuturesOrderRestClient.GetFuturesOrderTradesOptions { get; } = new GetFuturesOrderTradesOptions(_exchange, true); async Task> IFuturesOrderRestClient.GetFuturesOrderTradesAsync(GetOrderTradesRequest request, CancellationToken ct) { var validationError = SharedClient.GetFuturesOrderTradesOptions.ValidateRequest(request, this); @@ -1345,7 +1346,7 @@ async Task> IFuturesOrderRestClient.GetFuturesUser .ToArray(), nextPageRequest); } - EndpointOptions IFuturesOrderRestClient.CancelFuturesOrderOptions { get; } = new EndpointOptions(_exchange, true); + CancelFuturesOrderOptions IFuturesOrderRestClient.CancelFuturesOrderOptions { get; } = new CancelFuturesOrderOptions(_exchange, true); async Task> IFuturesOrderRestClient.CancelFuturesOrderAsync(CancelOrderRequest request, CancellationToken ct) { var validationError = SharedClient.CancelFuturesOrderOptions.ValidateRequest(request, this); @@ -1362,7 +1363,7 @@ async Task> IFuturesOrderRestClient.CancelFuturesOrderAsync return HttpResult.Ok(order, new SharedId(order.Data.OrderId.ToString())); } - EndpointOptions IFuturesOrderRestClient.GetPositionsOptions { get; } = new EndpointOptions(_exchange, true); + GetPositionsOptions IFuturesOrderRestClient.GetPositionsOptions { get; } = new GetPositionsOptions(_exchange, true); async Task> IFuturesOrderRestClient.GetPositionsAsync(GetPositionsRequest request, CancellationToken ct) { var validationError = SharedClient.GetPositionsOptions.ValidateRequest(request, this); @@ -1388,7 +1389,7 @@ async Task> IFuturesOrderRestClient.GetPositionsAsy }).ToArray()); } - EndpointOptions IFuturesOrderRestClient.ClosePositionOptions { get; } = new EndpointOptions(_exchange, true) + ClosePositionOptions IFuturesOrderRestClient.ClosePositionOptions { get; } = new ClosePositionOptions(_exchange, true) { RequiredOptionalParameters = new List { @@ -1418,7 +1419,7 @@ async Task> IFuturesOrderRestClient.ClosePositionAsync(Clos #endregion #region Fee Client - EndpointOptions IFeeRestClient.GetFeeOptions { get; } = new EndpointOptions(_exchange, false); + GetFeeOptions IFeeRestClient.GetFeeOptions { get; } = new GetFeeOptions(_exchange, false); async Task> IFeeRestClient.GetFeesAsync(GetFeeRequest request, CancellationToken ct) { @@ -1469,7 +1470,7 @@ async Task> ISpotTriggerOrderRestClient.PlaceSpotTriggerOrd return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions ISpotTriggerOrderRestClient.GetSpotTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true) + GetSpotTriggerOrderOptions ISpotTriggerOrderRestClient.GetSpotTriggerOrderOptions { get; } = new GetSpotTriggerOrderOptions(_exchange, true) { }; async Task> ISpotTriggerOrderRestClient.GetSpotTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) @@ -1542,7 +1543,7 @@ async Task> ISpotTriggerOrderRestClient.GetSp } } - EndpointOptions ISpotTriggerOrderRestClient.CancelSpotTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true); + CancelSpotTriggerOrderOptions ISpotTriggerOrderRestClient.CancelSpotTriggerOrderOptions { get; } = new CancelSpotTriggerOrderOptions(_exchange, true); async Task> ISpotTriggerOrderRestClient.CancelSpotTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) { var validationError = SharedClient.CancelSpotTriggerOrderOptions.ValidateRequest(request, this); @@ -1601,7 +1602,7 @@ private OrderSide GetOrderSide(PlaceFuturesTriggerOrderRequest request) return request.OrderDirection == SharedTriggerOrderDirection.Enter ? OrderSide.Sell : OrderSide.Buy; } - EndpointOptions IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true) + GetFuturesTriggerOrderOptions IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderOptions { get; } = new GetFuturesTriggerOrderOptions(_exchange, true) { }; async Task> IFuturesTriggerOrderRestClient.GetFuturesTriggerOrderAsync(GetOrderRequest request, CancellationToken ct) @@ -1690,7 +1691,7 @@ private SharedTriggerOrderStatus ParseTriggerOrderStatus(WhiteBitClosedOrder clo return SharedTriggerOrderStatus.Unknown; } - EndpointOptions IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderOptions { get; } = new EndpointOptions(_exchange, true); + CancelFuturesTriggerOrderOptions IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderOptions { get; } = new CancelFuturesTriggerOrderOptions(_exchange, true); async Task> IFuturesTriggerOrderRestClient.CancelFuturesTriggerOrderAsync(CancelOrderRequest request, CancellationToken ct) { var validationError = SharedClient.CancelFuturesTriggerOrderOptions.ValidateRequest(request, this); @@ -1713,7 +1714,7 @@ async Task> IFuturesTriggerOrderRestClient.CancelFuturesTri #endregion #region Tp/SL Client - EndpointOptions IFuturesTpSlRestClient.SetFuturesTpSlOptions { get; } = new EndpointOptions(_exchange, true) + SetFuturesTpSlOptions IFuturesTpSlRestClient.SetFuturesTpSlOptions { get; } = new SetFuturesTpSlOptions(_exchange, true) { RequiredOptionalParameters = new List { @@ -1742,7 +1743,7 @@ async Task> IFuturesTpSlRestClient.SetFuturesTpSlAsync(SetT return HttpResult.Ok(result, new SharedId(result.Data.OrderId.ToString())); } - EndpointOptions IFuturesTpSlRestClient.CancelFuturesTpSlOptions { get; } = new EndpointOptions(_exchange, true) + CancelFuturesTpSlOptions IFuturesTpSlRestClient.CancelFuturesTpSlOptions { get; } = new CancelFuturesTpSlOptions(_exchange, true) { RequiredOptionalParameters = new List { diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs index 9747ea1..12c6474 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiSubAccount.cs @@ -36,7 +36,7 @@ public async Task> CreateSubAccountAsync(string a permissions.Add("spotEnabled", spotEnabled); permissions.Add("collateralEnabled", collateralEnabled); parameters.Add("permissions", permissions); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/create", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/create", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -51,7 +51,7 @@ public async Task DeleteSubAccountAsync(string id, CancellationToken { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/delete", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/delete", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -71,7 +71,7 @@ public async Task EditSubAccountAsync(string id, string alias, strin permissions.Add("spotEnabled", spotEnabled); permissions.Add("collateralEnabled", collateralEnabled); parameters.Add("permissions", permissions); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/edit", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/edit", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -88,7 +88,7 @@ public async Task> GetSubAccountsAsync(string? s parameters.Add("search", search); parameters.Add("", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/list", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/list", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -106,7 +106,7 @@ public async Task SubaccountTransferAsync(string subaccountId, SubTr parameters.Add("direction", direction); parameters.Add("ticker", asset); parameters.Add("amount", quantity); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/transfer", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); ; var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -121,7 +121,7 @@ public async Task BlockSubaccountAsync(string id, CancellationToken { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/block", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/block", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -136,7 +136,7 @@ public async Task UnblockSubaccountAsync(string id, CancellationToke { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/unblock", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/unblock", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -152,7 +152,7 @@ public async Task> GetSubaccountBalancesAsync( var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("id", id); parameters.Add("ticker", asset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/balances", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/balances", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -172,7 +172,7 @@ public async Task> GetSubaccountBalancesAsync( public async Task> GetSubaccountTransferHistoryAsync(CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/sub-account/transfer/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/sub-account/transfer/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs index 4c962d3..8f14aff 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiTrading.cs @@ -91,7 +91,7 @@ public async Task> PlaceSpotOrderAsync( throw new ArgumentException("Unknown path for order type"); } - var request = _definitions.GetOrCreate(HttpMethod.Post, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, path, WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -108,7 +108,7 @@ public async Task[]>> PlaceSpotMult { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("orders", requests.ToArray()); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true); var resultData = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); if (!resultData.Success) return HttpResult.Fail[]>(resultData); @@ -149,7 +149,7 @@ public async Task> CancelOrderAsync(string symbol, lon parameters.Add("market", symbol); parameters.Add("orderId", orderId); parameters.Add("clientOrderId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/cancel", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -164,7 +164,7 @@ public async Task> CancelOrdersAsync(IE { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("orders", requests.ToArray()); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/cancel/bulk", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -179,8 +179,8 @@ public async Task CancelAllOrdersAsync(string? symbol = null, IEnume { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - parameters.Add("type", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/cancel/all", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + parameters.AddRaw("type", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/cancel/all", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -199,7 +199,7 @@ public async Task> GetOpenOrdersAsync(string? symbol parameters.Add("clientOrderId", clientOrderId); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/orders", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -230,7 +230,7 @@ public async Task>> GetClos parameters.Add("offset", offset); parameters.Add("startDate", startTime); parameters.Add("endDate", endTime); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/order/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/trade-account/order/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); if (!result.Success) @@ -262,7 +262,7 @@ public async Task> GetUserTradesAsync(string? sy parameters.Add("endTime", endDate); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/executed-history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/trade-account/executed-history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); if (symbol != null) { @@ -296,10 +296,13 @@ public async Task> GetOrderTradesAsync(long orde parameters.Add("orderId", orderId); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/trade-account/order", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/trade-account/order", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(12000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); - return HttpResult.Ok(result, result.Data?.Records); + if (!result.Success) + return HttpResult.Fail(result); + + return HttpResult.Ok(result, result.Data.Records); } #endregion @@ -317,7 +320,7 @@ public async Task> EditOrderAsync(string symbol, long? parameters.Add("total", quoteQuantity); parameters.Add("price", price); parameters.Add("activationPrice", triggerPrice); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/modify", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/modify", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -335,8 +338,8 @@ public async Task SetKillSwitchAsync(string symbol, int timeout, IEn #pragma warning disable CS8604 // Possible null reference argument. Works as intended parameters.Add("timeout", timeout == 0 ? null : timeout.ToString()); #pragma warning restore CS8604 // Possible null reference argument. - parameters.Add("types", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/kill-switch", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + parameters.AddRaw("types", orderProductTypes?.Select(EnumConverter.GetString).ToArray()); + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/kill-switch", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; @@ -351,7 +354,7 @@ public async Task> GetKillSwitchStatusAsync(str { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("market", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/order/kill-switch/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/order/kill-switch/status", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs index df52613..27f845d 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs @@ -526,7 +526,10 @@ private async Task> QueryAsync(string method, bool auth, C }, auth); var result = await QueryAsync(BaseAddress.AppendPath("ws"), query, ct).ConfigureAwait(false); - return WebSocketResult.Ok(result, result.Data == null ? default : result.Data.Result); + if (!result.Success) + return WebSocketResult.Fail(result); + + return WebSocketResult.Ok(result, result.Data.Result); } /// @@ -550,13 +553,13 @@ private async Task> QueryAsync(string method, bool auth, C private async Task> GetTokenAsync() { if (ApiCredentials == null) - return WebSocketResult.Fail(ExchangeName, new NoApiCredentialsError()); + return WebSocketResult.Fail(Exchange, new NoApiCredentialsError()); if (_tokenCache.TryGetValue(ApiCredentials.Key, out var token) && token.Expire > DateTime.UtcNow) - return new WebSocketResult(ExchangeName, token.Token, null); + return new WebSocketResult(Exchange, token.Token, null); if (ClientOptions.Environment.Name == "UnitTest") - return new WebSocketResult(ExchangeName, "123", null); + return new WebSocketResult(Exchange, "123", null); _logger.LogDebug("Requesting websocket token"); var restClient = new WhiteBitRestClient(x => @@ -569,11 +572,11 @@ private async Task> GetTokenAsync() if (!result.Success) { _logger.LogWarning("Failed to retrieve websocket token: {Error}", result.Error); - return WebSocketResult.Fail(ExchangeName, result.Error!); + return WebSocketResult.Fail(Exchange, result.Error!); } _tokenCache[ApiCredentials.Key] = new CachedToken { Token = result.Data, Expire = DateTime.UtcNow.AddSeconds(60) }; - return new WebSocketResult(ExchangeName, result.Data, null); + return new WebSocketResult(Exchange, result.Data, null); } private class CachedToken diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs index 1fc6100..b521336 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs @@ -19,16 +19,16 @@ internal partial class WhiteBitSocketClientV4Api : IWhiteBitSocketClientV4ApiSha private const string _exchange = "WhiteBit"; private const string _topicSpotId = "WhiteBitSpot"; private const string _topicFuturesId = "WhiteBitFutures"; - public string Exchange => _exchange; public TradingMode[] SupportedTradingModes => new[] { TradingMode.Spot, TradingMode.PerpetualLinear }; public TradingMode[] SupportedFuturesModes => new[] { TradingMode.PerpetualLinear }; public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); + public SharedClientInfo Discover() => SharedUtils.GetClientInfo(this); #region Balance client - EndpointOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new EndpointOptions(_exchange, true) + SubscribeBalanceOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new SubscribeBalanceOptions(_exchange, true) { OptionalExchangeParameters = new List { @@ -43,7 +43,7 @@ async Task> IBalanceSocketClient.SubscribeTo if (request.TradingMode == null || request.TradingMode == TradingMode.Spot) { - var assets = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); + var assets = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); if (assets == null) { // request all assets @@ -66,7 +66,7 @@ async Task> IBalanceSocketClient.SubscribeTo } else { - var assets = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); + var assets = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "BalanceAssets"); if (assets == null) { // request all assets @@ -94,7 +94,7 @@ async Task> IBalanceSocketClient.SubscribeTo #region Book Ticker client - EndpointOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new EndpointOptions(_exchange, false); + SubscribeBookTickerOptions IBookTickerSocketClient.SubscribeBookTickerOptions { get; } = new SubscribeBookTickerOptions(_exchange, false); async Task> IBookTickerSocketClient.SubscribeToBookTickerUpdatesAsync(SubscribeBookTickerRequest request, Action> handler, CancellationToken ct) { var validationError = SharedClient.SubscribeBookTickerOptions.ValidateRequest(request, this); @@ -178,7 +178,7 @@ async Task> ITickerSocketClient.SubscribeToT #region Trade client - EndpointOptions ITradeSocketClient.SubscribeTradeOptions { get; } = new EndpointOptions(_exchange, false) + SubscribeTradeOptions ITradeSocketClient.SubscribeTradeOptions { get; } = new SubscribeTradeOptions(_exchange, false) { SupportsMultipleSymbols = true, MaxSymbolCount = 100 @@ -210,7 +210,7 @@ async Task> ITradeSocketClient.SubscribeToTr #region User Trade client - EndpointOptions IUserTradeSocketClient.SubscribeUserTradeOptions { get; } = new EndpointOptions(_exchange, true) + SubscribeUserTradeOptions IUserTradeSocketClient.SubscribeUserTradeOptions { get; } = new SubscribeUserTradeOptions(_exchange, true) { OptionalExchangeParameters = new List { @@ -223,7 +223,7 @@ async Task> IUserTradeSocketClient.Subscribe if (validationError != null) return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "UserTradeSymbols"); + var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "UserTradeSymbols"); if (symbols == null) { // request all symbols @@ -270,7 +270,7 @@ async Task> IUserTradeSocketClient.Subscribe #region Spot Order client - EndpointOptions ISpotOrderSocketClient.SubscribeSpotOrderOptions { get; } = new EndpointOptions(_exchange, false) + SubscribeSpotOrderOptions ISpotOrderSocketClient.SubscribeSpotOrderOptions { get; } = new SubscribeSpotOrderOptions(_exchange, false) { OptionalExchangeParameters = new List { @@ -283,7 +283,7 @@ async Task> ISpotOrderSocketClient.Subscribe if (validationError != null) return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); + var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); if (symbols == null) { // request all symbols @@ -342,7 +342,7 @@ async Task> ISpotOrderSocketClient.Subscribe #endregion #region Position client - EndpointOptions IPositionSocketClient.SubscribePositionOptions { get; } = new EndpointOptions(_exchange, false); + SubscribePositionOptions IPositionSocketClient.SubscribePositionOptions { get; } = new SubscribePositionOptions(_exchange, false); async Task> IPositionSocketClient.SubscribeToPositionUpdatesAsync(SubscribePositionRequest request, Action> handler, CancellationToken ct) { var validationError = SharedClient.SubscribePositionOptions.ValidateRequest(request, this); @@ -373,7 +373,7 @@ async Task> IPositionSocketClient.SubscribeT #region Futures Order client - EndpointOptions IFuturesOrderSocketClient.SubscribeFuturesOrderOptions { get; } = new EndpointOptions(_exchange, false) + SubscribeFuturesOrderOptions IFuturesOrderSocketClient.SubscribeFuturesOrderOptions { get; } = new SubscribeFuturesOrderOptions(_exchange, false) { OptionalExchangeParameters = new List { @@ -386,7 +386,7 @@ async Task> IFuturesOrderSocketClient.Subscr if (validationError != null) return WebSocketResult.Fail(Exchange, validationError); - var symbols = ExchangeParameters.GetProcessValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); + var symbols = ExchangeParameters.GetValue>(request.ExchangeParameters, Exchange, "OrderSymbols"); if (symbols == null) { // request all symbols diff --git a/WhiteBit.Net/WhiteBitAuthenticationProvider.cs b/WhiteBit.Net/WhiteBitAuthenticationProvider.cs index 7321fd2..b5504c9 100644 --- a/WhiteBit.Net/WhiteBitAuthenticationProvider.cs +++ b/WhiteBit.Net/WhiteBitAuthenticationProvider.cs @@ -24,12 +24,12 @@ public WhiteBitAuthenticationProvider(WhiteBitCredentials credentials, INoncePro public override void ProcessRequest(RestApiClient apiClient, RestRequestConfiguration request) { - if (!request.Authenticated) + if (!request.RequestDefinition.Authenticated) return; var nonce = _nonceProvider.GetNonce().ToString(); request.BodyParameters ??= new Parameters(WhiteBitExchange._parameterSerializationSettings); - request.BodyParameters.Add("request", request.Path); + request.BodyParameters.Add("request", request.RequestDefinition.Path); request.BodyParameters.Add("nonce", nonce); request.BodyParameters.Add("nonceWindow", ((WhiteBitRestOptions)apiClient.ClientOptions).EnableNonceWindow); request.Headers ??= new Dictionary(); From 24bcde7f8099387e7c78473ad59c79663596bb37 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Wed, 10 Jun 2026 13:17:15 +0200 Subject: [PATCH 03/15] wip --- .../V4Api/WhiteBitSocketClientV4Api.cs | 38 +++++++++---------- .../V4Api/IWhiteBitSocketClientV4Api.cs | 20 +++++----- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs index 27f845d..999e5fb 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4Api.cs @@ -87,7 +87,7 @@ protected override WhiteBitAuthenticationProvider CreateAuthenticationProvider(W #region Trades /// - public async Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default) + public async Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default) { return await QueryAsync( "trades_request", @@ -129,7 +129,7 @@ public async Task> SubscribeToTradeUpdatesAs #region Last Price /// - public async Task> GetLastPriceAsync(string symbol, CancellationToken ct = default) + public async Task> GetLastPriceAsync(string symbol, CancellationToken ct = default) { var result = await QueryAsync( "lastprice_request", @@ -137,9 +137,9 @@ public async Task> GetLastPriceAsync(string symbol, Can ct, symbol).ConfigureAwait(false); if (!result.Success) - return WebSocketResult.Fail(result); + return QueryResult.Fail(result); - return WebSocketResult.Ok(result, result.Data ?? default); + return QueryResult.Ok(result, result.Data ?? default); } /// @@ -168,7 +168,7 @@ public async Task> SubscribeToLastPriceUpdat #region Ticker /// - public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) + public async Task> GetTickerAsync(string symbol, CancellationToken ct = default) { return await QueryAsync( "market_request", @@ -252,7 +252,7 @@ public async Task> SubscribeToBookTickerUpda #region Kline /// - public async Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default) + public async Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default) { return await QueryAsync( "candles_request", @@ -276,7 +276,7 @@ public async Task> SubscribeToKlineUpdatesAs #region Order book /// - public async Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default) + public async Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default) { depth.ValidateIntBetween(nameof(depth), 0, 100); @@ -303,7 +303,7 @@ public async Task> SubscribeToOrderBookUpdat #region Spot Balances /// - public async Task> GetSpotBalancesAsync(CancellationToken ct = default) + public async Task> GetSpotBalancesAsync(CancellationToken ct = default) { var result = await QueryAsync>( "balanceSpot_request", @@ -311,12 +311,12 @@ public async Task> GetSpotBalancesAsync( ct).ConfigureAwait(false); if (!result.Success) - return WebSocketResult.Fail(result); + return QueryResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return WebSocketResult.Ok(result, result.Data.Values.ToArray()); + return QueryResult.Ok(result, result.Data.Values.ToArray()); } /// @@ -330,7 +330,7 @@ public async Task> SubscribeToSpotBalanceUpd #region Margin Balances /// - public async Task> GetMarginBalancesAsync(CancellationToken ct = default) + public async Task> GetMarginBalancesAsync(CancellationToken ct = default) { var result = await QueryAsync>( "balanceMargin_request", @@ -338,12 +338,12 @@ public async Task> GetMarginBalancesAsy ct).ConfigureAwait(false); if (!result.Success) - return WebSocketResult.Fail(result); + return QueryResult.Fail(result); foreach (var item in result.Data) item.Value.Asset = item.Key; - return WebSocketResult.Ok(result, result.Data.Values.ToArray()); + return QueryResult.Ok(result, result.Data.Values.ToArray()); } /// @@ -357,7 +357,7 @@ public async Task> SubscribeToMarginBalanceU #region Open Orders /// - public async Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "ordersPending_request", @@ -379,7 +379,7 @@ public async Task> SubscribeToOpenOrderUpdat #region Closed Orders /// - public async Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "ordersExecuted_request", @@ -405,7 +405,7 @@ public async Task> SubscribeToClosedOrderUpd #region User Trades /// - public async Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) + public async Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default) { return await QueryAsync( "deals_request", @@ -516,7 +516,7 @@ public async Task> SubscribeToAccountBorrowE } #endregion - private async Task> QueryAsync(string method, bool auth, CancellationToken ct, params object[] parameters) + private async Task> QueryAsync(string method, bool auth, CancellationToken ct, params object[] parameters) { var query = new WhiteBitQuery(this, new WhiteBitSocketRequest { @@ -527,9 +527,9 @@ private async Task> QueryAsync(string method, bool auth, C var result = await QueryAsync(BaseAddress.AppendPath("ws"), query, ct).ConfigureAwait(false); if (!result.Success) - return WebSocketResult.Fail(result); + return QueryResult.Fail(result); - return WebSocketResult.Ok(result, result.Data.Result); + return QueryResult.Ok(result, result.Data.Result); } /// diff --git a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs index da417ca..5f46eeb 100644 --- a/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs +++ b/WhiteBit.Net/Interfaces/Clients/V4Api/IWhiteBitSocketClientV4Api.cs @@ -24,7 +24,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results /// Filter by returning results later than the trade with this id /// Cancellation token - Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default); + Task> GetTradeHistoryAsync(string symbol, int limit, long? fromId = null, CancellationToken ct = default); /// /// Subscribe to public trade updates @@ -71,7 +71,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// Symbol name /// Cancellation token - Task> GetLastPriceAsync(string symbol, CancellationToken ct = default); + Task> GetLastPriceAsync(string symbol, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -97,7 +97,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// Symbol name /// Cancellation token - Task> GetTickerAsync(string symbol, CancellationToken ct = default); + Task> GetTickerAsync(string symbol, CancellationToken ct = default); /// /// Subscribe to order book updates /// @@ -126,7 +126,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientFilter by start time /// Filter by end time /// Cancellation token - Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default); + Task> GetKlinesAsync(string symbol, KlineInterval interval, DateTime startTime, DateTime endTime, CancellationToken ct = default); /// /// Subscribe to order book updates @@ -147,7 +147,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientOrder book depth, max 100 /// 0 - no interval default. Available values: "0.00000001", "0.0000001", "0.000001", "0.00001", "0.0001", "0.001", "0.01", "0.1" /// Cancellation token - Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default); + Task> GetOrderBookAsync(string symbol, int depth, string? priceInterval = null, CancellationToken ct = default); /// /// Subscribe to order book updates @@ -166,7 +166,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// /// - Task> GetSpotBalancesAsync(CancellationToken ct = default); + Task> GetSpotBalancesAsync(CancellationToken ct = default); /// /// Subscribe to spot balance updates @@ -184,7 +184,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClient /// /// - Task> GetMarginBalancesAsync(CancellationToken ct = default); + Task> GetMarginBalancesAsync(CancellationToken ct = default); /// /// Subscribe to margin balance updates @@ -204,7 +204,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetOpenOrdersAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to open order updates @@ -226,7 +226,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes = null, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetClosedOrdersAsync(string symbol, IEnumerable? orderTypes = null, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to closed order updates @@ -247,7 +247,7 @@ public interface IWhiteBitSocketClientV4Api : ISocketApiClientMax number of results, max 100 /// Result offset /// Cancellation token for closing this subscription - Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); + Task> GetUserTradesAsync(string symbol, int? limit = null, int? offset = null, CancellationToken ct = default); /// /// Subscribe to user trade updates From d5613358c479dc8a4cee6ea2ab580d3a9675b290 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Wed, 10 Jun 2026 16:31:35 +0200 Subject: [PATCH 04/15] wip --- WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs index 6598448..fe47849 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiAccount.cs @@ -175,7 +175,7 @@ public async Task TransferAsync(AccountType fromAccount, AccountType public async Task> GetDepositWithdrawalHistoryAsync(TransactionType? type = null, string? asset = null, string? address = null, string? memo = null, string? addresses = null, string? uniqueId = null, int? limit = null, int? offset = null, CancellationToken ct = default) { var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); - parameters.AddAsInt("transactionMethod", type); + parameters.Add("transactionMethod", type, EnumSerialization.Number); parameters.Add("ticker", asset); parameters.Add("address", address); parameters.Add("memo", memo); From e7c1c4e98dc31bf0f3e7faeca24bd87fce7925db Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 15 Jun 2026 16:40:38 +0200 Subject: [PATCH 05/15] wip --- .../Clients/V4Api/WhiteBitRestClientV4Api.cs | 10 +- .../V4Api/WhiteBitSocketClientV4Api.cs | 5 +- WhiteBit.Net/Clients/WhiteBitRestClient.cs | 2 +- WhiteBit.Net/Clients/WhiteBitSocketClient.cs | 2 +- .../Clients/WhiteBitUserClientProvider.cs | 112 +++--------------- 5 files changed, 24 insertions(+), 107 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs index f281dd6..6c50a0f 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4Api.cs @@ -53,16 +53,16 @@ internal partial class WhiteBitRestClientV4Api : RestApiClient WhiteBitErrors.SocketErrors; #endregion - #region constructor/destructor /// /// ctor /// - internal WhiteBitSocketClientV4Api(ILogger logger, WhiteBitSocketOptions options) : - base(logger, WhiteBitExchange.ExchangeName, options.Environment.SocketClientAddress!, options, options.V4Options) + internal WhiteBitSocketClientV4Api(ILoggerFactory? loggerFactory, WhiteBitSocketOptions options) : + base(loggerFactory, WhiteBitExchange.ExchangeName, options.Environment.SocketClientAddress!, options, options.V4Options) { RateLimiter = WhiteBitExchange.RateLimiter.WhiteBitSocket; AllowTopicsOnTheSameConnection = false; diff --git a/WhiteBit.Net/Clients/WhiteBitRestClient.cs b/WhiteBit.Net/Clients/WhiteBitRestClient.cs index f22bc2d..a2476dc 100644 --- a/WhiteBit.Net/Clients/WhiteBitRestClient.cs +++ b/WhiteBit.Net/Clients/WhiteBitRestClient.cs @@ -45,7 +45,7 @@ public WhiteBitRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, { Initialize(options.Value); - V4Api = AddApiClient(new WhiteBitRestClientV4Api(_logger, httpClient, options.Value)); + V4Api = AddApiClient(new WhiteBitRestClientV4Api(loggerFactory, httpClient, options.Value)); } #endregion diff --git a/WhiteBit.Net/Clients/WhiteBitSocketClient.cs b/WhiteBit.Net/Clients/WhiteBitSocketClient.cs index f00813c..0a8ad84 100644 --- a/WhiteBit.Net/Clients/WhiteBitSocketClient.cs +++ b/WhiteBit.Net/Clients/WhiteBitSocketClient.cs @@ -43,7 +43,7 @@ public WhiteBitSocketClient(IOptions options, ILoggerFact { Initialize(options.Value); - V4Api = AddApiClient(new WhiteBitSocketClientV4Api(_logger, options.Value)); + V4Api = AddApiClient(new WhiteBitSocketClientV4Api(loggerFactory, options.Value)); } #endregion diff --git a/WhiteBit.Net/Clients/WhiteBitUserClientProvider.cs b/WhiteBit.Net/Clients/WhiteBitUserClientProvider.cs index 9eb5655..c367473 100644 --- a/WhiteBit.Net/Clients/WhiteBitUserClientProvider.cs +++ b/WhiteBit.Net/Clients/WhiteBitUserClientProvider.cs @@ -6,22 +6,22 @@ using System; using System.Collections.Concurrent; using System.Net.Http; +using CryptoExchange.Net.Clients; namespace WhiteBit.Net.Clients { /// - public class WhiteBitUserClientProvider : IWhiteBitUserClientProvider + public class WhiteBitUserClientProvider : UserClientProvider< + IWhiteBitRestClient, + IWhiteBitSocketClient, + WhiteBitRestOptions, + WhiteBitSocketOptions, + WhiteBitCredentials, + WhiteBitEnvironment + >, IWhiteBitUserClientProvider { - private ConcurrentDictionary _restClients = new ConcurrentDictionary(); - private ConcurrentDictionary _socketClients = new ConcurrentDictionary(); - - private readonly IOptions _restOptions; - private readonly IOptions _socketOptions; - private readonly HttpClient _httpClient; - private readonly ILoggerFactory? _loggerFactory; - /// - public string ExchangeName => WhiteBitExchange.ExchangeName; + public override string ExchangeName => WhiteBitExchange.ExchangeName; /// /// ctor @@ -40,97 +40,15 @@ public WhiteBitUserClientProvider( ILoggerFactory? loggerFactory, IOptions restOptions, IOptions socketOptions) + : base(httpClient, loggerFactory, restOptions, socketOptions) { - _httpClient = httpClient ?? new HttpClient(); - _httpClient.Timeout = restOptions.Value.RequestTimeout; - _loggerFactory = loggerFactory; - _restOptions = restOptions; - _socketOptions = socketOptions; } /// - public void InitializeUserClient(string userIdentifier, WhiteBitCredentials credentials, WhiteBitEnvironment? environment = null) - { - CreateRestClient(userIdentifier, credentials, environment); - CreateSocketClient(userIdentifier, credentials, environment); - } - - /// - public void ClearUserClients(string userIdentifier) - { - _restClients.TryRemove(userIdentifier, out _); - _socketClients.TryRemove(userIdentifier, out _); - } - + protected override IWhiteBitRestClient ConstructRestClient(HttpClient client, ILoggerFactory? loggerFactory, IOptions options) + => new WhiteBitRestClient(client, loggerFactory, options); /// - public IWhiteBitRestClient GetRestClient(string userIdentifier, WhiteBitCredentials? credentials = null, WhiteBitEnvironment? environment = null) - { - if (!_restClients.TryGetValue(userIdentifier, out var client) || client.Disposed) - client = CreateRestClient(userIdentifier, credentials, environment); - - return client; - } - - /// - public IWhiteBitSocketClient GetSocketClient(string userIdentifier, WhiteBitCredentials? credentials = null, WhiteBitEnvironment? environment = null) - { - if (!_socketClients.TryGetValue(userIdentifier, out var client) || client.Disposed) - client = CreateSocketClient(userIdentifier, credentials, environment); - - return client; - } - - private IWhiteBitRestClient CreateRestClient(string userIdentifier, WhiteBitCredentials? credentials, WhiteBitEnvironment? environment) - { - var clientRestOptions = SetRestEnvironment(environment); - var client = new WhiteBitRestClient(_httpClient, _loggerFactory, clientRestOptions); - if (credentials != null) - { - client.SetApiCredentials(credentials); - _restClients[userIdentifier] = client; - } - return client; - } - - private IWhiteBitSocketClient CreateSocketClient(string userIdentifier, WhiteBitCredentials? credentials, WhiteBitEnvironment? environment) - { - var clientSocketOptions = SetSocketEnvironment(environment); - var client = new WhiteBitSocketClient(clientSocketOptions!, _loggerFactory); - if (credentials != null) - { - client.SetApiCredentials(credentials); - _socketClients[userIdentifier] = client; - } - return client; - } - - private IOptions SetRestEnvironment(WhiteBitEnvironment? environment) - { - if (environment == null) - return _restOptions; - - var newRestClientOptions = new WhiteBitRestOptions(); - var restOptions = _restOptions.Value.Set(newRestClientOptions); - newRestClientOptions.Environment = environment; - return Options.Create(newRestClientOptions); - } - - private IOptions SetSocketEnvironment(WhiteBitEnvironment? environment) - { - if (environment == null) - return _socketOptions; - - var newSocketClientOptions = new WhiteBitSocketOptions(); - var restOptions = _socketOptions.Value.Set(newSocketClientOptions); - newSocketClientOptions.Environment = environment; - return Options.Create(newSocketClientOptions); - } - - private static T ApplyOptionsDelegate(Action? del) where T : new() - { - var opts = new T(); - del?.Invoke(opts); - return opts; - } + protected override IWhiteBitSocketClient ConstructSocketClient(ILoggerFactory? loggerFactory, IOptions options) + => new WhiteBitSocketClient(options, loggerFactory); } } From f175bbe7a545196b094660956488ee28e29c1db4 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Tue, 16 Jun 2026 14:54:58 +0200 Subject: [PATCH 06/15] wip --- AGENTS.md | 4 ++-- .../Interfaces/Clients/IWhiteBitUserClientProvider.cs | 5 +++++ llms-full.txt | 10 +++++----- llms.txt | 4 ++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9acc7d2..d302bb9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ description: Use WhiteBit.Net when generating C#/.NET code that interacts with t ## Quick Decision -If the user asks for WhiteBit API access in C#/.NET, use `WhiteBit.Net`. Do not write raw `HttpClient` calls to WhiteBit endpoints. The library handles authentication, request signing, rate limiting, response parsing, WebSocket reconnection, and the `WebCallResult` / `CallResult` error model. +If the user asks for WhiteBit API access in C#/.NET, use `WhiteBit.Net`. Do not write raw `HttpClient` calls to WhiteBit endpoints. The library handles authentication, request signing, rate limiting, response parsing, WebSocket reconnection, and the `HttpResult` / `WebSocketResult` error model. For multi-exchange code, use `CryptoExchange.Net.SharedApis` through `client.V4Api.SharedClient`. @@ -43,7 +43,7 @@ var restClient = new WhiteBitRestClient(options => ## Core Pattern: Result Handling -REST methods return `WebCallResult` or `WebCallResult`. WebSocket requests and subscriptions return `CallResult`. Always check `.Success` before reading `.Data`. +REST methods return `HttpResult` or `HttpResult`. WebSocket requests and subscriptions return `WebSocketResult`. Always check `.Success` before reading `.Data`. ```csharp var tickers = await publicClient.V4Api.ExchangeData.GetTickersAsync(); diff --git a/WhiteBit.Net/Interfaces/Clients/IWhiteBitUserClientProvider.cs b/WhiteBit.Net/Interfaces/Clients/IWhiteBitUserClientProvider.cs index 517d380..9737cec 100644 --- a/WhiteBit.Net/Interfaces/Clients/IWhiteBitUserClientProvider.cs +++ b/WhiteBit.Net/Interfaces/Clients/IWhiteBitUserClientProvider.cs @@ -21,6 +21,11 @@ public interface IWhiteBitUserClientProvider : IExchangeService /// public void ClearUserClients(string userIdentifier); + /// + /// Clear all client from the cache + /// + void Clear(); + /// /// Get the Rest client for a specific user. In case the client does not exist yet it will be created and the should be provided, unless has been called prior for this user. /// diff --git a/llms-full.txt b/llms-full.txt index 9db9882..7639111 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -24,8 +24,8 @@ Primary documentation: - Include `using WhiteBit.Net;` when using `WhiteBitCredentials` or `WhiteBitEnvironment`. - Direct client calls are rooted at `V4Api`; do not generate `SpotApi`, `FuturesApi`, or `GeneralApi` members. - Always check `.Success` before reading `.Data`. -- REST methods return `WebCallResult` or `WebCallResult`. -- WebSocket requests and subscriptions return `CallResult` or `CallResult`. +- REST methods return `HttpResult` or `HttpResult`. +- WebSocket requests and subscriptions return `WebSocketResult` or `CallResult`. - Reuse clients. Do not instantiate a REST or socket client per request in production code. - Prefer dependency injection for long-running services. - Store WebSocket subscriptions and unsubscribe during shutdown. @@ -163,9 +163,9 @@ Console.WriteLine(ticker.LastPrice); Retry only transient errors: ```csharp -async Task> WithRetry(Func>> call, int maxAttempts = 3) +async Task> WithRetry(Func>> call, int maxAttempts = 3) { - WebCallResult last = default!; + HttpResult last = default!; for (var attempt = 1; attempt <= maxAttempts; attempt++) { @@ -469,7 +469,7 @@ Examples/ai-friendly/04-multi-exchange.cs CryptoExchange.Net SharedApis REST and socket patterns. Examples/ai-friendly/05-error-handling.cs - WebCallResult pattern, transient retry, WhiteBit validation examples, symbol metadata handling. + HttpResult pattern, transient retry, WhiteBit validation examples, symbol metadata handling. ``` These files are intended to be copied into a console `Program.cs` after `dotnet add package WhiteBit.Net`. diff --git a/llms.txt b/llms.txt index 803f120..75233e4 100644 --- a/llms.txt +++ b/llms.txt @@ -2,7 +2,7 @@ > Strongly typed .NET client library for the WhiteBit REST and WebSocket APIs. Supports V4 public market data, spot trading, collateral/futures trading, balances, deposits, withdrawals, convert, WhiteBit Codes, sub-accounts, private WebSocket streams, local order books, and CryptoExchange.Net shared APIs. -WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `WebCallResult` / `CallResult` for error-aware results, supports client-side rate limiting, automatic WebSocket reconnection, dependency injection, native AOT, and shared API abstractions for multi-exchange applications. Current repository package version: 3.11.1. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. +WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `HttpResult` / `WebSocketResult` for error-aware results, supports client-side rate limiting, automatic WebSocket reconnection, dependency injection, native AOT, and shared API abstractions for multi-exchange applications. Current repository package version: 3.11.1. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. ## Documentation @@ -19,7 +19,7 @@ WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `WebCallResult - [Collateral/Futures Trading](https://github.com/JKorf/WhiteBit.Net/blob/main/Examples/ai-friendly/02-collateral-futures.cs): Leverage, collateral order, positions, reduce-only close - [WebSocket Streams](https://github.com/JKorf/WhiteBit.Net/blob/main/Examples/ai-friendly/03-websocket.cs): Public ticker/kline streams and private order/balance streams with teardown - [Multi-Exchange Pattern](https://github.com/JKorf/WhiteBit.Net/blob/main/Examples/ai-friendly/04-multi-exchange.cs): CryptoExchange.Net SharedApis usage -- [Error Handling](https://github.com/JKorf/WhiteBit.Net/blob/main/Examples/ai-friendly/05-error-handling.cs): `WebCallResult` checks, transient retry, validation handling +- [Error Handling](https://github.com/JKorf/WhiteBit.Net/blob/main/Examples/ai-friendly/05-error-handling.cs): `HttpResult` checks, transient retry, validation handling ## Critical Rules From 1938d94b5ba293f239288f37784c2ae6c6da4b9e Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 16 Jun 2026 20:48:25 +0200 Subject: [PATCH 07/15] docs --- AGENTS.md | 6 ++++-- Examples/ai-friendly/04-multi-exchange.cs | 2 ++ Examples/ai-friendly/05-error-handling.cs | 4 +++- docs/ai-api-map.md | 9 ++++++--- llms-full.txt | 15 ++++++++++----- llms.txt | 3 ++- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d302bb9..5c76121 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ description: Use WhiteBit.Net when generating C#/.NET code that interacts with t ## Quick Decision -If the user asks for WhiteBit API access in C#/.NET, use `WhiteBit.Net`. Do not write raw `HttpClient` calls to WhiteBit endpoints. The library handles authentication, request signing, rate limiting, response parsing, WebSocket reconnection, and the `HttpResult` / `WebSocketResult` error model. +If the user asks for WhiteBit API access in C#/.NET, use `WhiteBit.Net`. Do not write raw `HttpClient` calls to WhiteBit endpoints. The library handles authentication, request signing, rate limiting, response parsing, WebSocket reconnection, and the `HttpResult` / `QueryResult` / `WebSocketResult` error model. For multi-exchange code, use `CryptoExchange.Net.SharedApis` through `client.V4Api.SharedClient`. @@ -43,7 +43,7 @@ var restClient = new WhiteBitRestClient(options => ## Core Pattern: Result Handling -REST methods return `HttpResult` or `HttpResult`. WebSocket requests and subscriptions return `WebSocketResult`. Always check `.Success` before reading `.Data`. +REST methods return `HttpResult` or `HttpResult`. WebSocket request/response methods return `QueryResult`. WebSocket subscriptions return `WebSocketResult`. Always check `.Success` before reading `.Data`. ```csharp var tickers = await publicClient.V4Api.ExchangeData.GetTickersAsync(); @@ -180,6 +180,8 @@ Console.WriteLine(ticker.Data.LastPrice); Shared REST interfaces implemented by WhiteBit include spot symbols, spot tickers, recent trades, order book, balances, assets, deposits, withdrawals, spot orders, futures symbols, futures tickers, leverage, open interest, position history, futures orders, fees, trigger orders, TP/SL, book ticker, funding rate, and transfers. +Use `new WhiteBitRestClient().V4Api.SharedClient.Discover()` when code needs runtime metadata about supported shared interfaces and endpoint options. + ## Dependency Injection ```csharp diff --git a/Examples/ai-friendly/04-multi-exchange.cs b/Examples/ai-friendly/04-multi-exchange.cs index f545570..368ec4a 100644 --- a/Examples/ai-friendly/04-multi-exchange.cs +++ b/Examples/ai-friendly/04-multi-exchange.cs @@ -15,6 +15,7 @@ // WhiteBit exposes a `.SharedClient` property on V4Api. // SharedClient implements interfaces like ISpotTickerRestClient, ISpotOrderRestClient, // IBalanceRestClient, and more: a common abstraction across exchange libraries. +// Use SharedClient.Discover() when you need runtime capability metadata. ISpotTickerRestClient whiteBitShared = new WhiteBitRestClient().V4Api.SharedClient; @@ -58,6 +59,7 @@ async Task PrintTicker(ISpotTickerRestClient client, SharedSymbol symbol) // IPositionSocketClient // ---- WEBSOCKET EXAMPLE: SHARED SUBSCRIPTION ---- +// Shared socket subscriptions return WebSocketResult. var whiteBitSocket = new WhiteBitSocketClient(); ITickerSocketClient whiteBitTickerSocket = whiteBitSocket.V4Api.SharedClient; diff --git a/Examples/ai-friendly/05-error-handling.cs b/Examples/ai-friendly/05-error-handling.cs index 43a5e08..74c07a0 100644 --- a/Examples/ai-friendly/05-error-handling.cs +++ b/Examples/ai-friendly/05-error-handling.cs @@ -16,7 +16,9 @@ }); // ---- 1. THE BASIC PATTERN ---- -// Every REST method returns HttpResult or HttpResult. +// Every direct and SharedApis REST method returns HttpResult or HttpResult. +// WebSocket request/response methods return QueryResult. +// WebSocket subscriptions return WebSocketResult. // .Success is true/false. .Data is valid only when .Success is true. // .Error contains structured error info when .Success is false. diff --git a/docs/ai-api-map.md b/docs/ai-api-map.md index 1bf8d2a..06e0b3a 100644 --- a/docs/ai-api-map.md +++ b/docs/ai-api-map.md @@ -163,6 +163,7 @@ Use this file to route common user intents to the correct WhiteBit.Net client me | Shared order book socket | `IOrderBookSocketClient.SubscribeToOrderBookUpdatesAsync(...)` | | Shared user trade socket | `IUserTradeSocketClient.SubscribeToUserTradeUpdatesAsync(...)` | | Shared position socket | `IPositionSocketClient.SubscribeToPositionUpdatesAsync(...)` | +| Discover shared capabilities | `client.V4Api.SharedClient.Discover()` | For shared socket subscriptions, keep the concrete socket client and unsubscribe with `await socketClient.UnsubscribeAsync(subscription.Data)`. @@ -170,9 +171,11 @@ For shared socket subscriptions, keep the concrete socket client and unsubscribe | Situation | Pattern | |---|---| -| REST success check | `if (!result.Success) { Console.WriteLine(result.Error); return; }` | -| Socket subscription success check | `if (!sub.Success) { Console.WriteLine(sub.Error); return; }` | -| Read REST data | Read `result.Data` only after `result.Success` | +| REST success check | Direct and shared REST methods return `HttpResult` / `HttpResult` | +| Socket request success check | WebSocket request/response calls return `QueryResult` | +| Socket subscription success check | Direct and shared subscriptions return `WebSocketResult` | +| Generic success check | `if (!result.Success) { Console.WriteLine(result.Error); return; }` | +| Read result data | Read `result.Data` only after `result.Success` | | Retry decision | Retry only when `result.Error?.IsTransient == true` | | Cancellation | Pass `ct: cancellationToken` | diff --git a/llms-full.txt b/llms-full.txt index 7639111..4e2a24e 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -5,7 +5,7 @@ WhiteBit.Net is a strongly typed .NET client library for the WhiteBit REST and WebSocket APIs. It is part of the CryptoExchange.Net ecosystem and uses the same result, shared API, logging, rate limiting, and socket patterns as other JKorf exchange libraries. Current package version in this repository: 3.11.1. -CryptoExchange.Net dependency: 11.1.0. +CryptoExchange.Net dependency on this branch: 12.0.0-beta1. Target frameworks: netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. Native AOT: supported on compatible .NET targets. @@ -24,8 +24,10 @@ Primary documentation: - Include `using WhiteBit.Net;` when using `WhiteBitCredentials` or `WhiteBitEnvironment`. - Direct client calls are rooted at `V4Api`; do not generate `SpotApi`, `FuturesApi`, or `GeneralApi` members. - Always check `.Success` before reading `.Data`. -- REST methods return `HttpResult` or `HttpResult`. -- WebSocket requests and subscriptions return `WebSocketResult` or `CallResult`. +- Direct REST and SharedApis REST methods return `HttpResult` or `HttpResult`. +- WebSocket request/response methods return `QueryResult`. +- Direct and SharedApis WebSocket subscriptions return `WebSocketResult`. +- SharedApis helper methods that do not perform HTTP/socket I/O can return `ExchangeCallResult`. - Reuse clients. Do not instantiate a REST or socket client per request in production code. - Prefer dependency injection for long-running services. - Store WebSocket subscriptions and unsubscribe during shutdown. @@ -395,7 +397,7 @@ if (!ticker.Success) Console.WriteLine(ticker.Data.LastPrice); ``` -Shared WebSocket subscriptions return an `UpdateSubscription` in `.Data`. For shared interface variables, unsubscribe through the concrete socket client: +Shared WebSocket subscriptions return `WebSocketResult` with the subscription in `.Data`. For shared interface variables, unsubscribe through the concrete socket client: ```csharp var socketClient = new WhiteBitSocketClient(); @@ -418,6 +420,8 @@ await socketClient.UnsubscribeAsync(sub.Data); Do not call `tickerSocket.UnsubscribeAsync(...)` on `ITickerSocketClient`; that shared interface does not expose the method. +Shared clients also expose `Discover()` for runtime capability discovery. Use it when tooling or exchange-agnostic code needs to inspect implemented shared interfaces and endpoint option metadata instead of hard-coding assumptions. + ## Symbol And Quantity Handling For direct WhiteBit calls, use the exchange's symbol names exactly as returned by `GetSymbolsAsync()` or `GetFuturesSymbolsAsync()`. @@ -466,7 +470,7 @@ Examples/ai-friendly/03-websocket.cs Public ticker stream, kline stream, authenticated balance/order streams, success checks, teardown. Examples/ai-friendly/04-multi-exchange.cs - CryptoExchange.Net SharedApis REST and socket patterns. + CryptoExchange.Net SharedApis REST and socket patterns, including concrete socket-client teardown. Examples/ai-friendly/05-error-handling.cs HttpResult pattern, transient retry, WhiteBit validation examples, symbol metadata handling. @@ -481,6 +485,7 @@ Before finalizing generated code: - Does it compile with the current package? - Does it include `using WhiteBit.Net;` when using credentials or environments? - Does every REST/socket result check `.Success` before `.Data`? +- For socket request/response methods, does it expect `QueryResult` rather than `HttpResult`? - Does it use the correct API branch: `V4Api.ExchangeData`, `Account`, `Trading`, `CollateralTrading`, `Convert`, `Codes`, or `SubAccount`? - Does it avoid raw WhiteBit URLs and manual signing? - Does it avoid nonexistent `WhiteBitClient`, `SpotApi`, `FuturesApi`, and `GeneralApi` members? diff --git a/llms.txt b/llms.txt index 75233e4..8c20759 100644 --- a/llms.txt +++ b/llms.txt @@ -2,7 +2,7 @@ > Strongly typed .NET client library for the WhiteBit REST and WebSocket APIs. Supports V4 public market data, spot trading, collateral/futures trading, balances, deposits, withdrawals, convert, WhiteBit Codes, sub-accounts, private WebSocket streams, local order books, and CryptoExchange.Net shared APIs. -WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `HttpResult` / `WebSocketResult` for error-aware results, supports client-side rate limiting, automatic WebSocket reconnection, dependency injection, native AOT, and shared API abstractions for multi-exchange applications. Current repository package version: 3.11.1. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. +WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `HttpResult` for REST, `QueryResult` for WebSocket request/response calls, and `WebSocketResult` for WebSocket subscriptions. Shared REST APIs use the same `HttpResult` pattern. It supports client-side rate limiting, automatic WebSocket reconnection, dependency injection, native AOT, and shared API abstractions for multi-exchange applications. Current repository package version: 3.11.1. This branch targets CryptoExchange.Net 12.0.0-beta1. Targets netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0. ## Documentation @@ -31,6 +31,7 @@ WhiteBit.Net is part of the CryptoExchange.Net ecosystem. It uses `HttpResult - For spot orders use `restClient.V4Api.Trading.PlaceSpotOrderAsync`. - For collateral/futures orders use `restClient.V4Api.CollateralTrading.PlaceOrderAsync`. - For multi-exchange code use `CryptoExchange.Net.SharedApis` from `.V4Api.SharedClient`. +- Use `SharedClient.Discover()` when code needs to inspect implemented shared interfaces and endpoint options at runtime. - Store WebSocket subscriptions and call `socketClient.UnsubscribeAsync(subscription.Data)`. ## Reference From fccdde43ce0c145982d66380e231ab13a13bcb42 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Wed, 17 Jun 2026 16:12:16 +0200 Subject: [PATCH 08/15] wip --- WhiteBit.Net/WhiteBitUserDataTracker.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/WhiteBit.Net/WhiteBitUserDataTracker.cs b/WhiteBit.Net/WhiteBitUserDataTracker.cs index abb830c..2754c76 100644 --- a/WhiteBit.Net/WhiteBitUserDataTracker.cs +++ b/WhiteBit.Net/WhiteBitUserDataTracker.cs @@ -20,7 +20,6 @@ public WhiteBitUserSpotDataTracker( SpotUserDataTrackerConfig? config) : base( logger, restClient.V4Api.SharedClient, - null, restClient.V4Api.SharedClient, socketClient.V4Api.SharedClient, restClient.V4Api.SharedClient, @@ -48,7 +47,6 @@ public WhiteBitUserFuturesDataTracker( string? userIdentifier, FuturesUserDataTrackerConfig? config) : base(logger, restClient.V4Api.SharedClient, - null, restClient.V4Api.SharedClient, socketClient.V4Api.SharedClient, restClient.V4Api.SharedClient, From 1b8f0a33ea402ae32676f39a4e14a1910ee126cb Mon Sep 17 00:00:00 2001 From: Jkorf Date: Thu, 18 Jun 2026 14:55:18 +0200 Subject: [PATCH 09/15] wip --- WhiteBit.Net/WhiteBitExchange.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/WhiteBit.Net/WhiteBitExchange.cs b/WhiteBit.Net/WhiteBitExchange.cs index a3b6cbc..1c54145 100644 --- a/WhiteBit.Net/WhiteBitExchange.cs +++ b/WhiteBit.Net/WhiteBitExchange.cs @@ -102,7 +102,7 @@ public static string FormatSymbol(string baseAsset, string quoteAsset, TradingMo /// /// Rate limiter configuration for the WhiteBit API /// - public static WhiteBitRateLimiters RateLimiter { get; } = new WhiteBitRateLimiters(); + public static WhiteBitRateLimiters RateLimiter { get; set; } = new WhiteBitRateLimiters(); } /// @@ -121,13 +121,19 @@ public class WhiteBitRateLimiters public event Action RateLimitUpdated; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - internal WhiteBitRateLimiters() + /// + /// ctor + /// + public WhiteBitRateLimiters() #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { Initialize(); } - private void Initialize() + /// + /// Initialize the rate limits + /// + protected virtual void Initialize() { WhiteBit = new RateLimitGate("WhiteBit"); WhiteBitSocket = new RateLimitGate("WhiteBit Socket") From 14022c159054d0be2eed53c106f9889240587d0e Mon Sep 17 00:00:00 2001 From: Jkorf Date: Thu, 18 Jun 2026 16:34:52 +0200 Subject: [PATCH 10/15] wip --- .../V4Api/WhiteBitRestClientV4ApiShared.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs index 432dd73..4eaf3f7 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs @@ -426,7 +426,13 @@ async Task> IWithdrawalRestClient.GetWithdrawalsA return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data.Records, x => x.CreateTime, request.StartTime, request.EndTime, direction) .Select(x => - new SharedWithdrawal(x.Asset, x.Address, x.Quantity, x.TransactionStatus == Enums.TransactionStatus.Success, x.CreateTime) + new SharedWithdrawal( + x.Asset, + x.Address, + x.Quantity, + x.TransactionStatus == Enums.TransactionStatus.Success, + x.CreateTime, + GetWithdrawalStatus(x)) { Confirmations = x.Confirmations?.Actual, Network = x.Network, @@ -438,6 +444,23 @@ async Task> IWithdrawalRestClient.GetWithdrawalsA .ToArray(), nextPageRequest); } + private SharedTransferStatus GetWithdrawalStatus(WhiteBitDepositWithdrawal x) + { + if (x.TransactionStatus == TransactionStatus.Canceled || x.TransactionStatus == TransactionStatus.UnconfirmedByUser) + return SharedTransferStatus.Failed; + + if (x.TransactionStatus == TransactionStatus.Success || x.TransactionStatus == TransactionStatus.PartialSuccess) + return SharedTransferStatus.Completed; + + if (x.TransactionStatus == TransactionStatus.AwaitingVerification + || x.TransactionStatus == TransactionStatus.ConfirmationInProgress + || x.TransactionStatus == TransactionStatus.Frozen + || x.TransactionStatus == TransactionStatus.Pending + || x.TransactionStatus == TransactionStatus.Uncredited) + return SharedTransferStatus.InProgress; + + return SharedTransferStatus.Unknown; + } #endregion #region Withdraw client From 94b2e2efda58d88d4e0c629ae6f8589506911689 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Fri, 19 Jun 2026 13:48:10 +0200 Subject: [PATCH 11/15] wip --- .../V4Api/WhiteBitRestClientV4ApiShared.cs | 101 +++++++++++------- .../V4Api/WhiteBitSocketClientV4ApiShared.cs | 16 +-- WhiteBit.Net/WhiteBitExchange.cs | 3 +- 3 files changed, 73 insertions(+), 47 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs index 4eaf3f7..d315f5a 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs @@ -25,7 +25,7 @@ internal partial class WhiteBitRestClientV4Api : IWhiteBitRestClientV4ApiShared public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); - public SharedClientInfo Discover() => SharedUtils.GetClientInfo(this); + public SharedClientInfo Discover() => SharedUtils.GetClientInfo(WhiteBitExchange.Metadata, this); #region Spot Symbol client GetSpotSymbolsOptions ISpotSymbolRestClient.GetSpotSymbolsOptions { get; } = new GetSpotSymbolsOptions(_exchange, false); @@ -50,20 +50,20 @@ async Task> ISpotSymbolRestClient.GetSpotSymbolsA PriceDecimals = s.QuoteAssetPrecision }).ToArray()); - ExchangeSymbolCache.UpdateSymbolInfo(_topicSpotId, response.Data!); + ExchangeSymbolCache.UpdateSymbolInfo(_topicSpotId, EnvironmentName, null, response.Data!); return response; } async Task> ISpotSymbolRestClient.GetSpotSymbolsForBaseAssetAsync(string baseAsset) { - if (!ExchangeSymbolCache.HasCached(_topicSpotId)) + if (!ExchangeSymbolCache.HasCached(_topicSpotId, EnvironmentName, null)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicSpotId, baseAsset)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicSpotId, EnvironmentName, null, baseAsset)); } async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(SharedSymbol symbol) @@ -71,26 +71,26 @@ async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsy if (symbol.TradingMode != TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Only Spot symbols allowed"); - if (!ExchangeSymbolCache.HasCached(_topicSpotId)) + if (!ExchangeSymbolCache.HasCached(_topicSpotId, EnvironmentName, null)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbol)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, EnvironmentName, null, symbol)); } async Task> ISpotSymbolRestClient.SupportsSpotSymbolAsync(string symbolName) { - if (!ExchangeSymbolCache.HasCached(_topicSpotId)) + if (!ExchangeSymbolCache.HasCached(_topicSpotId, EnvironmentName, null)) { var symbols = await ((ISpotSymbolRestClient)this).GetSpotSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, symbolName)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicSpotId, EnvironmentName, null, symbolName)); } #endregion @@ -111,7 +111,14 @@ async Task> ISpotTickerRestClient.GetSpotTickerAsyn if (ticker == null) return HttpResult.Fail(result, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); - return HttpResult.Ok(result, new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, null, null, ticker.BaseVolume, ticker.ChangePercentage) + return HttpResult.Ok(result, new SharedSpotTicker( + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, ticker.Symbol), + ticker.Symbol, + ticker.LastPrice, + null, + null, + ticker.BaseVolume, + ticker.ChangePercentage) { QuoteVolume = ticker.QuoteVolume }); @@ -129,7 +136,15 @@ async Task> ISpotTickerRestClient.GetSpotTickersA return HttpResult.Fail(result); var data = result.Data.Where(x => !x.Symbol.EndsWith("_PERP")); - return HttpResult.Ok(result, data.Select(x => new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), x.Symbol, x.LastPrice, null, null, x.BaseVolume, x.ChangePercentage) + return HttpResult.Ok(result, data.Select(x => + new SharedSpotTicker( + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, x.Symbol), + x.Symbol, + x.LastPrice, + null, + null, + x.BaseVolume, + x.ChangePercentage) { QuoteVolume = x.QuoteVolume }).ToArray()); @@ -151,7 +166,7 @@ async Task> IBookTickerRestClient.GetBookTickerAsyn return HttpResult.Fail(resultTicker); return HttpResult.Ok(resultTicker, new SharedBookTicker( - ExchangeSymbolCache.ParseSymbol(request.Symbol.TradingMode == TradingMode.Spot ? _topicSpotId : _topicFuturesId, resultTicker.Data.Symbol), + ExchangeSymbolCache.ParseSymbol(request.Symbol.TradingMode == TradingMode.Spot ? _topicSpotId : _topicFuturesId, EnvironmentName, null, resultTicker.Data.Symbol), resultTicker.Data.Symbol, resultTicker.Data.Asks[0].Price, resultTicker.Data.Asks[0].Quantity, @@ -548,7 +563,7 @@ async Task> ISpotOrderRestClient.GetSpotOrderAsync(G if (openOrder != null) { return HttpResult.Ok(openOrders, new SharedSpotOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, openOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), ParseOrderType(openOrder.OrderType, openOrder.PostOnly), @@ -581,7 +596,7 @@ async Task> ISpotOrderRestClient.GetSpotOrderAsync(G var status = closedOrder.Status == OrderStatus.Canceled ? SharedOrderStatus.Canceled : SharedOrderStatus.Filled; return HttpResult.Ok(closeOrders, new SharedSpotOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, closedOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), ParseOrderType(closedOrder.OrderType, closedOrder.PostOnly), @@ -618,7 +633,7 @@ async Task> ISpotOrderRestClient.GetOpenSpotOrders var data = orders.Data.Where(x => !x.Symbol.EndsWith("_PERP")); return HttpResult.Ok(orders, data.Select(x => new SharedSpotOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, x.Symbol), x.Symbol, x.OrderId.ToString(), ParseOrderType(x.OrderType, x.PostOnly), @@ -675,7 +690,7 @@ async Task> ISpotOrderRestClient.GetClosedSpotOrde TimeSpan.FromDays(180)); var data = result.Data.Where(x => !x.Key.EndsWith("_PERP")).SelectMany(xk => xk.Value.Select(x => new SharedSpotOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, x.Symbol), xk.Key, x.OrderId.ToString(), ParseOrderType(x.OrderType, x.PostOnly), @@ -713,7 +728,7 @@ async Task> ISpotOrderRestClient.GetSpotOrderTrade return HttpResult.Fail(orders); return HttpResult.Ok(orders, orders.Data.Select(x => new SharedUserTrade( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, x.Symbol), x.Symbol, x.OrderId.ToString(), x.Id.ToString(), @@ -765,7 +780,7 @@ async Task> ISpotOrderRestClient.GetSpotUserTrades return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) .Select(y => new SharedUserTrade( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, y.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, y.Symbol), y.Symbol, y.OrderId.ToString(), y.Id.ToString(), @@ -851,20 +866,20 @@ async Task> IFuturesSymbolRestClient.GetFuture }; }).ToArray()); - ExchangeSymbolCache.UpdateSymbolInfo(_topicFuturesId, response.Data!); + ExchangeSymbolCache.UpdateSymbolInfo(_topicFuturesId, EnvironmentName, null, response.Data!); return response; } async Task> IFuturesSymbolRestClient.GetFuturesSymbolsForBaseAssetAsync(string baseAsset) { - if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) + if (!ExchangeSymbolCache.HasCached(_topicFuturesId, EnvironmentName, null)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicFuturesId, baseAsset)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.GetSymbolsForBaseAsset(_topicFuturesId, EnvironmentName, null, baseAsset)); } async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(SharedSymbol symbol) @@ -872,26 +887,26 @@ async Task> IFuturesSymbolRestClient.SupportsFuturesSym if (symbol.TradingMode == TradingMode.Spot) throw new ArgumentException(nameof(symbol), "Spot symbols not allowed"); - if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) + if (!ExchangeSymbolCache.HasCached(_topicFuturesId, EnvironmentName, null)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbol)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, EnvironmentName, null, symbol)); } async Task> IFuturesSymbolRestClient.SupportsFuturesSymbolAsync(string symbolName) { - if (!ExchangeSymbolCache.HasCached(_topicFuturesId)) + if (!ExchangeSymbolCache.HasCached(_topicFuturesId, EnvironmentName, null)) { var symbols = await ((IFuturesSymbolRestClient)this).GetFuturesSymbolsAsync(new GetSymbolsRequest()).ConfigureAwait(false); if (!symbols.Success) return ExchangeCallResult.Fail(Exchange, symbols.Error!); } - return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, symbolName)); + return ExchangeCallResult.Ok(Exchange, ExchangeSymbolCache.SupportsSymbol(_topicFuturesId, EnvironmentName, null, symbolName)); } #endregion @@ -914,7 +929,7 @@ async Task> IFuturesTickerRestClient.GetFuturesT if (ticker == null) return HttpResult.Fail(resultTicker, new ServerError(new ErrorInfo(ErrorType.UnknownSymbol, "Symbol not found"))); - return HttpResult.Ok(resultTicker, new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, ticker.Symbol), ticker.Symbol, ticker.LastPrice, ticker.HighPrice, ticker.LowPrice, ticker.BaseVolume, null) + return HttpResult.Ok(resultTicker, new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, ticker.Symbol), ticker.Symbol, ticker.LastPrice, ticker.HighPrice, ticker.LowPrice, ticker.BaseVolume, null) { IndexPrice = ticker.IndexPrice, FundingRate = ticker.FundingRate, @@ -935,7 +950,13 @@ async Task> IFuturesTickerRestClient.GetFuture return HttpResult.Ok(resultTickers, resultTickers.Data.Select(x => { - return new SharedFuturesTicker(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, x.LastPrice, x.HighPrice, x.LowPrice, x.BaseVolume, null) + return new SharedFuturesTicker( + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), + x.Symbol, + x.LastPrice, + x.HighPrice, + x.LowPrice, + x.BaseVolume, null) { IndexPrice = x.IndexPrice, FundingRate = x.FundingRate, @@ -1043,7 +1064,7 @@ async Task> IPositionHistoryRestClient.GetPo return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.OpenTime, request.StartTime, request.EndTime, direction) .Select(x => new SharedPositionHistory( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), x.Symbol, x.Quantity >= 0 ? SharedPositionSide.Long : SharedPositionSide.Short, x.BasePrice, @@ -1118,7 +1139,7 @@ async Task> IFuturesOrderRestClient.GetFuturesOrd if (openOrder != null) { return HttpResult.Ok(openOrders, new SharedFuturesOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, openOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), ParseOrderType(openOrder.OrderType, openOrder.PostOnly), @@ -1156,7 +1177,7 @@ async Task> IFuturesOrderRestClient.GetFuturesOrd : SharedOrderStatus.Filled; return HttpResult.Ok(closeOrders, new SharedFuturesOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, closedOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), ParseOrderType(closedOrder.OrderType, closedOrder.PostOnly), @@ -1196,7 +1217,7 @@ async Task> IFuturesOrderRestClient.GetOpenFutu var data = orders.Data.Where(x => x.Symbol.EndsWith("_PERP")); return HttpResult.Ok(orders, [.. data.Select(x => new SharedFuturesOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), x.Symbol, x.OrderId.ToString(), ParseOrderType(x.OrderType, x.PostOnly), @@ -1256,7 +1277,7 @@ async Task> IFuturesOrderRestClient.GetClosedFu TimeSpan.FromDays(180)); var data = result.Data.Where(x => x.Key.EndsWith("_PERP")).SelectMany(xk => xk.Value.Select(x => new SharedFuturesOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, xk.Key), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, xk.Key), xk.Key, x.OrderId.ToString(), ParseOrderType(x.OrderType, x.PostOnly), @@ -1299,7 +1320,7 @@ async Task> IFuturesOrderRestClient.GetFuturesOrde return HttpResult.Fail(orders); return HttpResult.Ok(orders, orders.Data.Select(x => new SharedUserTrade( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), request.Symbol!.GetSymbol(FormatSymbol), x.OrderId.ToString(), x.Id.ToString(), @@ -1352,7 +1373,7 @@ async Task> IFuturesOrderRestClient.GetFuturesUser return HttpResult.Ok(result, ExchangeHelpers.ApplyFilter(result.Data, x => x.Time, request.StartTime, request.EndTime, direction) .Select(y => new SharedUserTrade( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, y.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, y.Symbol), y.Symbol, y.OrderId.ToString(), y.Id.ToString(), @@ -1400,7 +1421,11 @@ async Task> IFuturesOrderRestClient.GetPositionsAsy var data = result.Data; var resultTypes = request.Symbol == null && request.TradingMode == null ? SupportedTradingModes : request.Symbol != null ? new[] { request.Symbol.TradingMode } : new[] { request.TradingMode!.Value }; return HttpResult.Ok(result, data.Select(x => - new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, Math.Abs(x.Quantity), x.UpdateTime) + new SharedPosition( + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), + x.Symbol, + Math.Abs(x.Quantity), + x.UpdateTime) { UnrealizedPnl = x.Pnl, LiquidationPrice = x.LiquidationPrice == 0 ? null : x.LiquidationPrice, @@ -1513,7 +1538,7 @@ async Task> ISpotTriggerOrderRestClient.GetSp if (openOrder != null) { return HttpResult.Ok(openOrders, new SharedSpotTriggerOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, openOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), ParseOrderType(openOrder.OrderType, openOrder.PostOnly), @@ -1544,7 +1569,7 @@ async Task> ISpotTriggerOrderRestClient.GetSp var closedOrder = closeOrders.Data.Single().Value.Single(); return HttpResult.Ok(closeOrders, new SharedSpotTriggerOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, closedOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), ParseOrderType(closedOrder.OrderType, closedOrder.PostOnly), @@ -1645,7 +1670,7 @@ async Task> IFuturesTriggerOrderRestClient if (openOrder != null) { return HttpResult.Ok(openOrders, new SharedFuturesTriggerOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, openOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, openOrder.Symbol), openOrder.Symbol, openOrder.OrderId.ToString(), ParseOrderType(openOrder.OrderType, openOrder.PostOnly), @@ -1678,7 +1703,7 @@ async Task> IFuturesTriggerOrderRestClient var closedOrder = closeOrders.Data.Single().Value.Single(); return HttpResult.Ok(closeOrders, new SharedFuturesTriggerOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, closedOrder.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, closedOrder.Symbol), closedOrder.Symbol, closedOrder.OrderId.ToString(), ParseOrderType(closedOrder.OrderType, closedOrder.PostOnly), diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs index b521336..4fc87c3 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs @@ -25,7 +25,7 @@ internal partial class WhiteBitSocketClientV4Api : IWhiteBitSocketClientV4ApiSha public void SetDefaultExchangeParameter(string key, object value) => ExchangeParameters.SetStaticParameter(Exchange, key, value); public void ResetDefaultExchangeParameters() => ExchangeParameters.ResetStaticParameters(); - public SharedClientInfo Discover() => SharedUtils.GetClientInfo(this); + public SharedClientInfo Discover() => SharedUtils.GetClientInfo(WhiteBitExchange.Metadata, this); #region Balance client SubscribeBalanceOptions IBalanceSocketClient.SubscribeBalanceOptions { get; } = new SubscribeBalanceOptions(_exchange, true) @@ -105,7 +105,7 @@ async Task> IBookTickerSocketClient.Subscrib var result = await SubscribeToBookTickerUpdatesAsync(symbol, update => { handler(update.ToType(new SharedBookTicker( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, update.Data.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, update.Data.Symbol), update.Data.Symbol, update.Data.BestAskPrice, update.Data.BestAskQuantity, @@ -167,7 +167,7 @@ async Task> ITickerSocketClient.SubscribeToT var symbols = request.Symbols?.Length > 0 ? request.Symbols.Select(x => x.GetSymbol(FormatSymbol)).ToArray() : [request.Symbol!.GetSymbol(FormatSymbol)]; var result = await SubscribeToTickerUpdatesAsync(symbols, update => handler(update.ToType( - new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, update.Data.Symbol), update.Data.Symbol, update.Data.Ticker.LastPrice, update.Data.Ticker.HighPrice, update.Data.Ticker.LowPrice, update.Data.Ticker.Volume, update.Data.Ticker.OpenPrice == 0 ? null : Math.Round(update.Data.Ticker.ClosePrice / update.Data.Ticker.OpenPrice * 100 - 100, 2)) + new SharedSpotTicker(ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, update.Data.Symbol), update.Data.Symbol, update.Data.Ticker.LastPrice, update.Data.Ticker.HighPrice, update.Data.Ticker.LowPrice, update.Data.Ticker.Volume, update.Data.Ticker.OpenPrice == 0 ? null : Math.Round(update.Data.Ticker.ClosePrice / update.Data.Ticker.OpenPrice * 100 - 100, 2)) { QuoteVolume = update.Data.Ticker.QuoteVolume })), ct).ConfigureAwait(false); @@ -197,7 +197,7 @@ async Task> ITradeSocketClient.SubscribeToTr return; handler(update.ToType(update.Data.Trades.Select(x => - new SharedTrade(ExchangeSymbolCache.ParseSymbol(_topicSpotId, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, update.Data.Symbol), update.Data.Symbol, x.Quantity, x.Price, x.Timestamp) + new SharedTrade(ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, update.Data.Symbol), update.Data.Symbol, x.Quantity, x.Price, x.Timestamp) { Side = x.Side == Enums.OrderSide.Buy ? SharedOrderSide.Buy : SharedOrderSide.Sell } ).ToArray())); @@ -249,7 +249,7 @@ async Task> IUserTradeSocketClient.Subscribe handler(update.ToType([ new SharedUserTrade( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, update.Data.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, update.Data.Symbol) ?? ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, update.Data.Symbol), update.Data.Symbol, update.Data.OrderId.ToString(), update.Data.Id.ToString(), @@ -313,7 +313,7 @@ async Task> ISpotOrderSocketClient.Subscribe handler(update.ToType(new[] { new SharedSpotOrder( - ExchangeSymbolCache.ParseSymbol(_topicSpotId, update.Data.Order.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicSpotId, EnvironmentName, null, update.Data.Order.Symbol), update.Data.Order.Symbol, update.Data.Order.OrderId.ToString(), ParseOrderType(update.Data.Order.OrderType, update.Data.Order.PostOnly), @@ -355,7 +355,7 @@ async Task> IPositionSocketClient.SubscribeT if (update.UpdateType == SocketUpdateType.Snapshot) return; - handler(update.ToType(update.Data.Records.Select(x => new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, x.Symbol), x.Symbol, Math.Abs(x.Quantity), x.UpdateTime) + handler(update.ToType(update.Data.Records.Select(x => new SharedPosition(ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, x.Symbol), x.Symbol, Math.Abs(x.Quantity), x.UpdateTime) { AverageOpenPrice = x.BasePrice, PositionMode = SharedPositionMode.OneWay, @@ -417,7 +417,7 @@ async Task> IFuturesOrderSocketClient.Subscr handler(update.ToType(new[] { new SharedFuturesOrder( - ExchangeSymbolCache.ParseSymbol(_topicFuturesId, update.Data.Order.Symbol), + ExchangeSymbolCache.ParseSymbol(_topicFuturesId, EnvironmentName, null, update.Data.Order.Symbol), update.Data.Order.Symbol, update.Data.Order.OrderId.ToString(), ParseOrderType(update.Data.Order.OrderType, update.Data.Order.PostOnly), diff --git a/WhiteBit.Net/WhiteBitExchange.cs b/WhiteBit.Net/WhiteBitExchange.cs index 1c54145..b7732b9 100644 --- a/WhiteBit.Net/WhiteBitExchange.cs +++ b/WhiteBit.Net/WhiteBitExchange.cs @@ -27,7 +27,8 @@ public static class WhiteBitExchange "https://www.whitebit.com", ["https://docs.whitebit.com/"], PlatformType.CryptoCurrencyExchange, - CentralizationType.Centralized + CentralizationType.Centralized, + WhiteBitEnvironment.All ); /// From 22801f9925577c38c57eae275cac3b2cc5f55593 Mon Sep 17 00:00:00 2001 From: JKorf Date: Sun, 21 Jun 2026 20:57:20 +0200 Subject: [PATCH 12/15] wip --- WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs index b044b28..20b9170 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiConvert.cs @@ -58,7 +58,7 @@ public async Task> ConfirmConvertAsync(string /// public async Task> GetConvertHistoryAsync(string? fromAsset = null, string? toAsset = null, string? quoteId = null, DateTime? startTime = null, DateTime? endTime = null, int? limit = null, int? offset = null, CancellationToken ct = default) { - var parameters = new ParameterCollection(); + var parameters = new Parameters(WhiteBitExchange._parameterSerializationSettings); parameters.Add("fromTicker", fromAsset); parameters.Add("toTicker", toAsset); parameters.Add("quoteId", quoteId); @@ -66,7 +66,7 @@ public async Task> GetConvertHistoryAsync(str parameters.Add("to", endTime); parameters.Add("limit", limit); parameters.Add("offset", offset); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/api/v4/convert/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, + var request = _definitions.GetOrCreate(HttpMethod.Post, _baseClient.BaseAddress, "/api/v4/convert/history", WhiteBitExchange.RateLimiter.WhiteBit, 1, true, limitGuard: new SingleLimitGuard(10000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result; From dffa63fcd91ea9679ae0efd061c6fa189923abc2 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 22 Jun 2026 16:39:17 +0200 Subject: [PATCH 13/15] wip --- .../Clients/V4Api/WhiteBitRestClientV4ApiShared.cs | 9 ++++++--- .../Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs | 7 +++++-- .../Converters/WhiteBitSourceGenerationContext.cs | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs index d315f5a..96b6e14 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitRestClientV4ApiShared.cs @@ -241,7 +241,8 @@ async Task> IBalanceRestClient.GetBalancesAsync(GetB if (!result.Success) return HttpResult.Fail(result); - return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Asset, x.Available, x.Available + x.Frozen)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => + new SharedBalance(TradingMode.Spot, x.Asset, x.Available, x.Available + x.Frozen)).ToArray()); } else if(request.AccountType == SharedAccountType.Funding) { @@ -249,7 +250,8 @@ async Task> IBalanceRestClient.GetBalancesAsync(GetB if (!result.Success) return HttpResult.Fail(result); - return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Asset, x.MainBalance, x.MainBalance)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => + new SharedBalance([], x.Asset, x.MainBalance, x.MainBalance)).ToArray()); } else { @@ -257,7 +259,8 @@ async Task> IBalanceRestClient.GetBalancesAsync(GetB if (!result.Success) return HttpResult.Fail(result); - return HttpResult.Ok(result, result.Data.Select(x => new SharedBalance(x.Key, x.Value, x.Value)).ToArray()); + return HttpResult.Ok(result, result.Data.Select(x => + new SharedBalance(SupportedFuturesModes, x.Key, x.Value, x.Value)).ToArray()); } } diff --git a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs index 4fc87c3..b3ddb9a 100644 --- a/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs +++ b/WhiteBit.Net/Clients/V4Api/WhiteBitSocketClientV4ApiShared.cs @@ -60,7 +60,8 @@ async Task> IBalanceSocketClient.SubscribeTo var result = await SubscribeToSpotBalanceUpdatesAsync( assets!, - update => handler(update.ToType(update.Data.Select(x => new SharedBalance(x.Key, x.Value.Available, x.Value.Available + x.Value.Frozen)).ToArray())), + update => handler(update.ToType(update.Data.Select(x => + new SharedBalance(TradingMode.Spot, x.Key, x.Value.Available, x.Value.Available + x.Value.Frozen)).ToArray())), ct: ct).ConfigureAwait(false); return result; } @@ -84,7 +85,9 @@ async Task> IBalanceSocketClient.SubscribeTo var result = await SubscribeToMarginBalanceUpdatesAsync( assets, - update => handler(update.ToType(update.Data.Select(x => new SharedBalance(x.Asset, x.AvailableWithoutBorrow, x.Balance)).ToArray())), + update => handler(update.ToType(update.Data.Select(x => + new SharedBalance( + SupportedFuturesModes, x.Asset, x.AvailableWithoutBorrow, x.Balance)).ToArray())), ct: ct).ConfigureAwait(false); return result; } diff --git a/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs b/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs index f6b630d..3235cbb 100644 --- a/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs +++ b/WhiteBit.Net/Converters/WhiteBitSourceGenerationContext.cs @@ -19,6 +19,7 @@ namespace WhiteBit.Net.Converters [JsonSerializable(typeof(WhiteBitSocketResponse>))] [JsonSerializable(typeof(WhiteBitSocketResponse>))] [JsonSerializable(typeof(Parameters))] + [JsonSerializable(typeof(Parameters[]))] [JsonSerializable(typeof(WhiteBitSocketResponse))] [JsonSerializable(typeof(WhiteBitSocketResponse))] [JsonSerializable(typeof(WhiteBitSocketResponse))] From 88d990abc3173571c93a1221beb3bf6c14b0bf45 Mon Sep 17 00:00:00 2001 From: JKorf Date: Sun, 28 Jun 2026 18:01:03 +0200 Subject: [PATCH 14/15] Added europe environment --- WhiteBit.Net/Objects/WhiteBitApiAddresses.cs | 9 +++++++++ WhiteBit.Net/WhiteBitEnvironment.cs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/WhiteBit.Net/Objects/WhiteBitApiAddresses.cs b/WhiteBit.Net/Objects/WhiteBitApiAddresses.cs index 76eebde..3a090b8 100644 --- a/WhiteBit.Net/Objects/WhiteBitApiAddresses.cs +++ b/WhiteBit.Net/Objects/WhiteBitApiAddresses.cs @@ -22,5 +22,14 @@ public class WhiteBitApiAddresses RestClientAddress = "https://whitebit.com", SocketClientAddress = "wss://api.whitebit.com" }; + + /// + /// The addresses to connect to the WhiteBit Europe API + /// + public static WhiteBitApiAddresses Europe = new WhiteBitApiAddresses + { + RestClientAddress = "https://whitebit.eu", + SocketClientAddress = "wss://api.whitebit.eu" + }; } } diff --git a/WhiteBit.Net/WhiteBitEnvironment.cs b/WhiteBit.Net/WhiteBitEnvironment.cs index 858062a..c2d8fc9 100644 --- a/WhiteBit.Net/WhiteBitEnvironment.cs +++ b/WhiteBit.Net/WhiteBitEnvironment.cs @@ -43,6 +43,7 @@ public WhiteBitEnvironment() : base(TradeEnvironmentNames.Live) => name switch { TradeEnvironmentNames.Live => Live, + "Europe" => Europe, "" => Live, null => Live, _ => default @@ -62,6 +63,14 @@ public WhiteBitEnvironment() : base(TradeEnvironmentNames.Live) WhiteBitApiAddresses.Default.RestClientAddress, WhiteBitApiAddresses.Default.SocketClientAddress); + /// + /// Europe environment + /// + public static WhiteBitEnvironment Europe { get; } + = new WhiteBitEnvironment("Europe", + WhiteBitApiAddresses.Europe.RestClientAddress, + WhiteBitApiAddresses.Europe.SocketClientAddress); + /// /// Create a custom environment /// From 36254d5386974d48c48116aff87f29bea68c0f96 Mon Sep 17 00:00:00 2001 From: Jkorf Date: Mon, 29 Jun 2026 11:51:25 +0200 Subject: [PATCH 15/15] CryptoExchange.Net ref --- WhiteBit.Net/WhiteBit.Net.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WhiteBit.Net/WhiteBit.Net.csproj b/WhiteBit.Net/WhiteBit.Net.csproj index 79eff46..0cb8d54 100644 --- a/WhiteBit.Net/WhiteBit.Net.csproj +++ b/WhiteBit.Net/WhiteBit.Net.csproj @@ -53,12 +53,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - - -