From 3a1bd387e4b3aa7667512d455ad3dcf8ea731840 Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 2 Jul 2024 16:21:25 +0200 Subject: [PATCH 1/2] CryptoExchange update, added tp/sl parameters perpetual futures order placement, updated ratelimit --- BingX.Net/BingX.Net.csproj | 6 +- BingX.Net/BingX.Net.xml | 78 +++++++- BingX.Net/BingXAuthenticationProvider.cs | 9 +- BingX.Net/BingXExchange.cs | 4 +- ...ngXRestClientPerpetualFuturesApiAccount.cs | 40 ++-- ...ngXRestClientPerpetualFuturesApiTrading.cs | 187 ++++++++++-------- .../SpotApi/BingXRestClientSpotApiAccount.cs | 43 ++-- .../SpotApi/BingXRestClientSpotApiTrading.cs | 31 ++- ...ngXRestClientPerpetualFuturesApiTrading.cs | 60 ++++-- .../Models/BingXFuturesPlaceOrderRequest.cs | 72 +++++-- 10 files changed, 364 insertions(+), 166 deletions(-) diff --git a/BingX.Net/BingX.Net.csproj b/BingX.Net/BingX.Net.csproj index 6ad3cbb..046fd98 100644 --- a/BingX.Net/BingX.Net.csproj +++ b/BingX.Net/BingX.Net.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1 10.0 @@ -48,10 +48,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/BingX.Net/BingX.Net.xml b/BingX.Net/BingX.Net.xml index 8af19e5..6de8383 100644 --- a/BingX.Net/BingX.Net.xml +++ b/BingX.Net/BingX.Net.xml @@ -275,10 +275,10 @@ - + - + @@ -1550,7 +1550,7 @@ Cancellation token - + Place a new test order. Order won't actually get placed @@ -1564,6 +1564,16 @@ Reduce only Stop price Trailing percentage (between 0 and 1) + Stop loss order type + Stop loss trigger price + Stop loss order price + Stop loss trigger price type + Stop loss stop guaranteed + Take profit order type + Take profit trigger price + Take profit order price + Take profit trigger price type + Take profit stop guaranteed Time in force Close the position Trigger price @@ -1572,7 +1582,7 @@ Cancellation token - + Place a new order @@ -1586,6 +1596,16 @@ Reduce only Stop price Trailing percentage (between 0 and 1) + Stop loss order type + Stop loss trigger price + Stop loss order price + Stop loss trigger price type + Stop loss stop guaranteed + Take profit order type + Take profit trigger price + Take profit order price + Take profit trigger price type + Take profit stop guaranteed Time in force Close the position Trigger price @@ -3850,6 +3870,56 @@ Stop guaranteed + + + Stop loss order parameters + + + + + Take profit order parameters + + + + + Internal serialization parameter, use StopLoss for setting stop loss parameters + + + + + Internal serialization parameter, use TakeProfit for setting take profit parameters + + + + + Stop order info + + + + + Order type + + + + + Stop price + + + + + Price + + + + + Trigger type + + + + + Stop guarenteed + + 24h Price statistics diff --git a/BingX.Net/BingXAuthenticationProvider.cs b/BingX.Net/BingXAuthenticationProvider.cs index 7f257e4..5c49679 100644 --- a/BingX.Net/BingXAuthenticationProvider.cs +++ b/BingX.Net/BingXAuthenticationProvider.cs @@ -7,6 +7,8 @@ using CryptoExchange.Net.Clients; using CryptoExchange.Net.Objects; using CryptoExchange.Net.Converters.SystemTextJson; +using System.Linq; +using System.Globalization; namespace BingX.Net { @@ -44,7 +46,12 @@ public override void AuthenticateRequest( parameters.Add("recvWindow", (int)receiveWindow.TotalMilliseconds); } - var parameterSignData = parameters.CreateParamString(true, arraySerialization); + string parameterSignData; + if (parameterPosition == HttpMethodParameterPosition.InBody) + parameterSignData = string.Join("&", parameters.OrderBy(p => p.Key).Select(o => o.Key + "=" + string.Format(CultureInfo.InvariantCulture, "{0}", o.Value))); + else + parameterSignData = parameters.CreateParamString(true, arraySerialization); + parameters.Add("signature", SignHMACSHA256(parameterSignData, SignOutputType.Hex)); } } diff --git a/BingX.Net/BingXExchange.cs b/BingX.Net/BingXExchange.cs index 9fc1160..c4a518a 100644 --- a/BingX.Net/BingXExchange.cs +++ b/BingX.Net/BingXExchange.cs @@ -55,7 +55,9 @@ internal BingXRateLimiters() private void Initialize() { RestMarket = new RateLimitGate("Spot Rest Market") - .AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new List(), 100, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); // IP limit of 100 requests per 10 seconds in total + .AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new List(), 10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding)) // As suggested by BingX API support: IP limit of 10 requests per 1 second in total + .AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new List(), 100, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)) // IP limit of 100 requests per 10 seconds in total + .AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new List(), 500, TimeSpan.FromSeconds(60), RateLimitWindowType.Sliding)); // IP limit of 500 requests per 60 seconds in total RestAccount1 = new RateLimitGate("Spot Rest Account 1") .AddGuard(new RateLimitGuard(RateLimitGuard.PerHost, new List(), 1000, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)) // IP limit of 1000 requests per 10 seconds in total .AddGuard(new RateLimitGuard(RateLimitGuard.PerEndpoint, new List(), 100, TimeSpan.FromSeconds(10), RateLimitWindowType.Sliding)); // IP limit of 100 requests per 10 seconds per endpoint diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiAccount.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiAccount.cs index 68f96bd..2f74594 100644 --- a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiAccount.cs +++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiAccount.cs @@ -3,6 +3,7 @@ using BingX.Net.Objects.Internal; using BingX.Net.Objects.Models; using CryptoExchange.Net.Objects; +using CryptoExchange.Net.RateLimiting.Guards; using System; using System.Collections.Generic; using System.Net.Http; @@ -27,7 +28,8 @@ internal BingXRestClientPerpetualFuturesApiAccount(BingXRestClientPerpetualFutur /// public async Task> GetBalancesAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/balance", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/balance", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); return result.As(result.Data?.Balance); } @@ -46,7 +48,8 @@ public async Task>> GetIncomesAsync(strin parameters.AddOptionalMilliseconds("endTime", endTime); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/income", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/income", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync>(request, null, ct).ConfigureAwait(false); if (result && result.Data == null) { @@ -63,7 +66,8 @@ public async Task>> GetIncomesAsync(strin /// public async Task> GetTradingFeesAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/commissionRate", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/commissionRate", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); return result.As(result.Data?.Rates); } @@ -75,7 +79,8 @@ public async Task> GetTradingFeesAsync(Ca /// public async Task> StartUserStreamAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendRawAsync(request, null, ct).ConfigureAwait(false); return result.As(result.Data?.ListenKey); } @@ -91,7 +96,8 @@ public async Task KeepAliveUserStreamAsync(string listenKey, Canc { { "listenKey", listenKey } }; - var request = _definitions.GetOrCreate(HttpMethod.Put, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Put, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -106,7 +112,8 @@ public async Task StopUserStreamAsync(string listenKey, Cancellat { { "listenKey", listenKey } }; - var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -121,7 +128,8 @@ public async Task> GetMarginModeAsync(string symb { { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/marginType", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/marginType", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -137,7 +145,8 @@ public async Task SetMarginModeAsync(string symbol, MarginMode ma { "symbol", symbol } }; parameters.AddEnum("marginType", marginMode); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/marginType", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/marginType", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -152,7 +161,8 @@ public async Task> GetLeverageAsync(string symbol, { { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/leverage", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/leverage", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -170,7 +180,8 @@ public async Task> SetLeverageAsync(string sy }; parameters.AddEnum("side", side); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/leverage", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/leverage", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -189,7 +200,8 @@ public async Task AdjustIsolatedMarginAsync(string symbol, decima parameters.AddEnum("type", direction); parameters.AddEnum("positionSide", side); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/positionMargin", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/positionMargin", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -204,7 +216,8 @@ public async Task> GetPositionModeAsync(string { { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v1/positionSide/dual", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v1/positionSide/dual", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -221,7 +234,8 @@ public async Task> SetPositionModeAsync(string }; parameters.AddEnum("dualSidePosition", positionMode); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v1/positionSide/dual", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v1/positionSide/dual", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } diff --git a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiTrading.cs b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiTrading.cs index 253d591..271d187 100644 --- a/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiTrading.cs +++ b/BingX.Net/Clients/PerpetualFuturesApi/BingXRestClientPerpetualFuturesApiTrading.cs @@ -11,6 +11,7 @@ using System; using System.Linq; using System.Data.Common; +using CryptoExchange.Net.RateLimiting.Guards; namespace BingX.Net.Clients.PerpetualFuturesApi { @@ -36,7 +37,8 @@ public async Task>> GetPositionsAsync(s var parameters = new ParameterCollection(); parameters.AddOptional("symbol", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/positions", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/user/positions", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } @@ -56,17 +58,17 @@ public async Task> PlaceTestOrderAsync( decimal? stopPrice = null, decimal? priceRate = null, - //TakeProfitStopLossMode? stopLossType = null, - //decimal? stopLossStopPrice = null, - //decimal? stopLossPrice = null, - //TriggerType? stopLossTriggerType = null, - //bool? stopLossStopGuaranteed = null, + TakeProfitStopLossMode? stopLossType = null, + decimal? stopLossStopPrice = null, + decimal? stopLossPrice = null, + TriggerType? stopLossTriggerType = null, + bool? stopLossStopGuaranteed = null, - //TakeProfitStopLossMode? takeProfitType = null, - //decimal? takeProfitStopPrice = null, - //decimal? takeProfitPrice = null, - //TriggerType? takeProfitTriggerType = null, - //bool? takeProfitStopGuaranteed = null, + TakeProfitStopLossMode? takeProfitType = null, + decimal? takeProfitStopPrice = null, + decimal? takeProfitPrice = null, + TriggerType? takeProfitTriggerType = null, + bool? takeProfitStopGuaranteed = null, TimeInForce? timeInForce = null, bool? closePosition = null, @@ -93,30 +95,30 @@ public async Task> PlaceTestOrderAsync( parameter.AddOptional("activationPrice", triggerPrice); parameter.AddOptional("stopGuaranteed", stopGuaranteed); - // TODO how to pass this? - //if (stopLossType != null) - //{ - // var stopLossParams = new ParameterCollection(); - // stopLossParams.AddEnum("type", stopLossType); - // stopLossParams.AddOptional("stopPrice", stopLossStopPrice); - // stopLossParams.AddOptional("price", stopLossPrice); - // stopLossParams.AddOptionalEnum("workingType", stopLossTriggerType); - // stopLossParams.AddOptional("stopGuaranteed", stopLossStopGuaranteed); - // parameter.Add("stopLoss", new SystemTextJsonMessageSerializer().Serialize(stopLossParams)); - //} - - //if (takeProfitType != null) - //{ - // var stopLossParams = new ParameterCollection(); - // stopLossParams.AddEnum("type", takeProfitType); - // stopLossParams.AddOptional("stopPrice", takeProfitStopPrice); - // stopLossParams.AddOptional("price", takeProfitPrice); - // stopLossParams.AddOptionalEnum("workingType", takeProfitTriggerType); - // stopLossParams.AddOptional("stopGuaranteed", takeProfitStopGuaranteed); - // parameter.Add("takeProfit", new SystemTextJsonMessageSerializer().Serialize(stopLossParams)); - //} - - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/order/test", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + if (stopLossType != null) + { + var stopLossParams = new ParameterCollection(); + stopLossParams.AddEnum("type", stopLossType); + stopLossParams.AddOptional("stopPrice", stopLossStopPrice); + stopLossParams.AddOptional("price", stopLossPrice); + stopLossParams.AddOptionalEnum("workingType", stopLossTriggerType); + stopLossParams.AddOptional("stopGuaranteed", stopLossStopGuaranteed); + parameter.Add("stopLoss", new SystemTextJsonMessageSerializer().Serialize(stopLossParams)); + } + + if (takeProfitType != null) + { + var takeProfitParams = new ParameterCollection(); + takeProfitParams.AddEnum("type", takeProfitType); + takeProfitParams.AddOptional("stopPrice", takeProfitStopPrice); + takeProfitParams.AddOptional("price", takeProfitPrice); + takeProfitParams.AddOptionalEnum("workingType", takeProfitTriggerType); + takeProfitParams.AddOptional("stopGuaranteed", takeProfitStopGuaranteed); + parameter.Add("takeProfit", new SystemTextJsonMessageSerializer().Serialize(takeProfitParams)); + } + + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/order/test", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct, additionalHeaders: new Dictionary { { "X-SOURCE-KEY", _brokerId } @@ -140,17 +142,17 @@ public async Task> PlaceOrderAsync( decimal? stopPrice = null, decimal? priceRate = null, - //TakeProfitStopLossMode? stopLossType = null, - //decimal? stopLossStopPrice = null, - //decimal? stopLossPrice = null, - //TriggerType? stopLossTriggerType = null, - //bool? stopLossStopGuaranteed = null, + TakeProfitStopLossMode? stopLossType = null, + decimal? stopLossStopPrice = null, + decimal? stopLossPrice = null, + TriggerType? stopLossTriggerType = null, + bool? stopLossStopGuaranteed = null, - //TakeProfitStopLossMode? takeProfitType = null, - //decimal? takeProfitStopPrice = null, - //decimal? takeProfitPrice = null, - //TriggerType? takeProfitTriggerType = null, - //bool? takeProfitStopGuaranteed = null, + TakeProfitStopLossMode? takeProfitType = null, + decimal? takeProfitStopPrice = null, + decimal? takeProfitPrice = null, + TriggerType? takeProfitTriggerType = null, + bool? takeProfitStopGuaranteed = null, TimeInForce? timeInForce = null, bool? closePosition = null, @@ -177,30 +179,30 @@ public async Task> PlaceOrderAsync( parameter.AddOptional("activationPrice", triggerPrice); parameter.AddOptional("stopGuaranteed", stopGuaranteed); - // TODO how to pass this? - //if (stopLossType != null) - //{ - // var stopLossParams = new ParameterCollection(); - // stopLossParams.AddEnum("type", stopLossType); - // stopLossParams.AddOptional("stopPrice", stopLossStopPrice); - // stopLossParams.AddOptional("price", stopLossPrice); - // stopLossParams.AddOptionalEnum("workingType", stopLossTriggerType); - // stopLossParams.AddOptional("stopGuaranteed", stopLossStopGuaranteed); - // parameter.Add("stopLoss", Uri.EscapeDataString(new SystemTextJsonMessageSerializer().Serialize(stopLossParams))); - //} - - //if (takeProfitType != null) - //{ - // var stopLossParams = new ParameterCollection(); - // stopLossParams.AddEnum("type", takeProfitType); - // stopLossParams.AddOptional("stopPrice", takeProfitStopPrice); - // stopLossParams.AddOptional("price", takeProfitPrice); - // stopLossParams.AddOptionalEnum("workingType", takeProfitTriggerType); - // stopLossParams.AddOptional("stopGuaranteed", takeProfitStopGuaranteed); - // parameter.Add("takeProfit", Uri.EscapeDataString(new SystemTextJsonMessageSerializer().Serialize(stopLossParams))); - //} - - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + if (stopLossType != null) + { + var stopLossParams = new ParameterCollection(); + stopLossParams.AddEnum("type", stopLossType); + stopLossParams.AddOptional("stopPrice", stopLossStopPrice); + stopLossParams.AddOptional("price", stopLossPrice); + stopLossParams.AddOptionalEnum("workingType", stopLossTriggerType); + stopLossParams.AddOptional("stopGuaranteed", stopLossStopGuaranteed); + parameter.Add("stopLoss", new SystemTextJsonMessageSerializer().Serialize(stopLossParams)); + } + + if (takeProfitType != null) + { + var takeProfitParams = new ParameterCollection(); + takeProfitParams.AddEnum("type", takeProfitType); + takeProfitParams.AddOptional("stopPrice", takeProfitStopPrice); + takeProfitParams.AddOptional("price", takeProfitPrice); + takeProfitParams.AddOptionalEnum("workingType", takeProfitTriggerType); + takeProfitParams.AddOptional("stopGuaranteed", takeProfitStopGuaranteed); + parameter.Add("takeProfit", new SystemTextJsonMessageSerializer().Serialize(takeProfitParams)); + } + + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct, additionalHeaders: new Dictionary { { "X-SOURCE-KEY", _brokerId } @@ -217,12 +219,21 @@ public async Task>> PlaceMultipleOr IEnumerable orders, CancellationToken ct = default) { + foreach(var order in orders) + { + if (order.StopLoss != null) + order.StopLossStr = new SystemTextJsonMessageSerializer().Serialize(order.StopLoss); + + if (order.TakeProfit != null) + order.TakeProfitStr = new SystemTextJsonMessageSerializer().Serialize(order.TakeProfit); + } var parameter = new ParameterCollection() { { "batchOrders", new SystemTextJsonMessageSerializer().Serialize(orders) } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct, additionalHeaders: new Dictionary { { "X-SOURCE-KEY", _brokerId } @@ -243,7 +254,8 @@ public async Task> GetOrderAsync(string }; parameters.AddOptional("orderId", orderId); parameters.AddOptional("clientOrderId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As(result.Data?.Order); } @@ -261,7 +273,8 @@ public async Task> CancelOrderAsync(stri }; parameters.AddOptional("orderId", orderId); parameters.AddOptional("clientOrderId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As(result.Data?.Order); } @@ -275,7 +288,8 @@ public async Task> CloseAllPositionsAsy { var parameters = new ParameterCollection(); parameters.AddOptional("symbol", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/closeAllPositions", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/closeAllPositions", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -292,7 +306,8 @@ public async Task> CancelMultipleOrderAsync( }; parameters.AddOptional("orderIdList", orderIds?.Select(x => (object)x).ToArray()); parameters.AddOptional("clientOrderIDList", clientOrderIds?.Select(x => (object)x).ToArray()); - var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -305,7 +320,8 @@ public async Task> CancelAllOrderAsync(strin { var parameters = new ParameterCollection(); parameters.AddOptional("symbol", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/allOpenOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/swap/v2/trade/allOpenOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -319,7 +335,8 @@ public async Task>> GetOpenO var parameters = new ParameterCollection(); parameters.AddOptional("symbol", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/openOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/openOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -338,7 +355,8 @@ public async Task>> GetLiqui parameters.AddOptionalMilliseconds("endTime", endTime); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/forceOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/forceOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -357,7 +375,8 @@ public async Task>> GetClose parameters.AddOptionalMilliseconds("endTime", endTime); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/allOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/allOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -373,7 +392,8 @@ public async Task>> GetUserTrad parameters.AddOptional("orderId", orderId); parameters.AddOptionalMilliseconds("startTs", startTime); parameters.AddOptionalMilliseconds("endTs", endTime); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/allFillOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v2/trade/allFillOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Trades); } @@ -390,7 +410,8 @@ public async Task> CancelAllOrdersAfterAsy { "type", activate ? "ACTIVATE": "CLOSE" }, { "timeOut", cancelAfterSeconds } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/cancelAllAfter", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v2/trade/cancelAllAfter", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameter, ct).ConfigureAwait(false); } @@ -405,7 +426,8 @@ public async Task> ClosePositionAsync(st { { "positionId", positionId } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v1/trade/closePosition", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/swap/v1/trade/closePosition", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -420,7 +442,8 @@ public async Task>> GetPositi { { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v1/maintMarginRatio", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/swap/v1/maintMarginRatio", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } diff --git a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiAccount.cs b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiAccount.cs index d4d0284..7bd9e7d 100644 --- a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiAccount.cs +++ b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiAccount.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using BingX.Net.Objects.Internal; +using CryptoExchange.Net.RateLimiting.Guards; namespace BingX.Net.Clients.SpotApi { @@ -27,7 +28,8 @@ internal BingXRestClientSpotApiAccount(BingXRestClientSpotApi baseClient) /// public async Task>> GetBalancesAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/account/balance", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/account/balance", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, null, ct).ConfigureAwait(false); return result.As>(result.Data?.Balances); } @@ -46,7 +48,8 @@ public async Task>> GetDepositHistoryAsy parameters.AddOptionalMilliseconds("endTime", endTime); parameters.AddOptional("offset", offset); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/capital/deposit/hisrec", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/capital/deposit/hisrec", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendRawAsync>(request, parameters, ct).ConfigureAwait(false); } @@ -67,7 +70,8 @@ public async Task>> GetWithdrawalHist parameters.AddOptional("offset", offset); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/capital/withdraw/history", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/capital/withdraw/history", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendRawAsync>(request, parameters, ct).ConfigureAwait(false); } @@ -81,7 +85,8 @@ public async Task>> GetAssetsAsync(string? var parameters = new ParameterCollection(); parameters.AddOptional("coin", asset); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/config/getall", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/config/getall", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync>(request, parameters, ct).ConfigureAwait(false); } @@ -103,7 +108,8 @@ public async Task> WithdrawAsync(string asset parameters.AddOptional("addressTag", addressTag); parameters.AddOptional("withdrawOrderId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/wallets/v1/capital/withdraw/apply", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/wallets/v1/capital/withdraw/apply", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -121,7 +127,8 @@ public async Task> GetDepositAddressAsync(s parameters.AddOptional("offset", offset); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/deposit/address", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/deposit/address", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -139,7 +146,8 @@ public async Task> TransferAsync(TransferT }; parameters.AddEnum("type", tranferType); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/api/v3/post/asset/transfer", BingXExchange.RateLimiter.RestAccount2, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/api/v3/post/asset/transfer", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendRawAsync(request, parameters, ct).ConfigureAwait(false); if (!result) return result; @@ -164,7 +172,8 @@ public async Task> GetTransfersAsync(TransferType parameters.AddOptional("current", page); parameters.AddOptional("size", pageSize); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/asset/transfer", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/api/v3/asset/transfer", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendRawAsync(request, parameters, ct).ConfigureAwait(false); } @@ -186,7 +195,8 @@ public async Task> TransferInternalAsync(string asset, Ac parameters.AddOptional("callingCode", areaCode); parameters.AddOptional("transferClientId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/wallets/v1/capital/innerTransfer/apply", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/wallets/v1/capital/innerTransfer/apply", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -207,7 +217,8 @@ public async Task> GetInternalTransfersAsy parameters.AddOptional("offset", offset); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/innerTransfer/records", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/wallets/v1/capital/innerTransfer/records", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -218,7 +229,8 @@ public async Task> GetInternalTransfersAsy /// public async Task> StartUserStreamAsync(CancellationToken ct = default) { - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendRawAsync(request, null, ct).ConfigureAwait(false); return result.As(result.Data?.ListenKey); } @@ -234,7 +246,8 @@ public async Task KeepAliveUserStreamAsync(string listenKey, Canc { { "listenKey", listenKey } }; - var request = _definitions.GetOrCreate(HttpMethod.Put, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Put, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -249,7 +262,8 @@ public async Task StopUserStreamAsync(string listenKey, Cancellat { { "listenKey", listenKey } }; - var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Delete, "/openApi/user/auth/userDataStream", BingXExchange.RateLimiter.RestAccount1, 1, false, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -265,7 +279,8 @@ public async Task> GetTradingFeesAsync(string sy { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/user/commissionRate", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/user/commissionRate", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } diff --git a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiTrading.cs b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiTrading.cs index a868857..612a94c 100644 --- a/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiTrading.cs +++ b/BingX.Net/Clients/SpotApi/BingXRestClientSpotApiTrading.cs @@ -11,6 +11,7 @@ using CryptoExchange.Net.CommonObjects; using CryptoExchange.Net.Converters.SystemTextJson; using System.Data.Common; +using CryptoExchange.Net.RateLimiting.Guards; namespace BingX.Net.Clients.SpotApi { @@ -47,7 +48,8 @@ public async Task> PlaceOrderAsync(string symbol, Orde parameters.AddOptional("stopPrice", stopPrice); parameters.AddOptional("newClientOrderId", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/order", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct, additionalHeaders: new Dictionary { @@ -70,7 +72,8 @@ public async Task>> PlaceMultipleOrdersAsy { "data", new SystemTextJsonMessageSerializer().Serialize(orders) } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/batchOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct, additionalHeaders: new Dictionary { @@ -92,7 +95,8 @@ public async Task> CancelOrderAsync(string symbol, lon }; parameter.AddOptional("orderId", orderId); parameter.AddOptional("clientOrderID", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancel", BingXExchange.RateLimiter.RestAccount2, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancel", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct).ConfigureAwait(false); if (result) _baseClient.InvokeOrderCanceled(new OrderId { Id = result.Data.OrderId.ToString(), SourceObject = result.Data }); @@ -113,7 +117,8 @@ public async Task>> CancelOrdersAsync(stri parameter.AddOptional("orderIds", orderIds == null? null: string.Join(",", orderIds)); parameter.AddOptional("clientOrderIDs", clientOrderIds == null ? null : string.Join(",", clientOrderIds)); - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -130,7 +135,8 @@ public async Task>> CancelAllOrdersAsync(s { "symbol", symbol } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelOpenOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelOpenOrders", BingXExchange.RateLimiter.RestAccount2, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameter, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -148,7 +154,8 @@ public async Task> CancelAllOrdersAfterAsy { "timeOut", cancelAfterSeconds } }; - var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelAllAfter", BingXExchange.RateLimiter.RestAccount1, 1, true, 2, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Post, "/openApi/spot/v1/trade/cancelAllAfter", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(2, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameter, ct).ConfigureAwait(false); } @@ -166,7 +173,8 @@ public async Task> GetOrderAsync(string symbol, parameters.AddOptional("orderId", orderId); parameters.AddOptional("clientOrderID", clientOrderId); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/query", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/query", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); return await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); } @@ -180,7 +188,8 @@ public async Task>> GetOpenOrdersAs var parameters = new ParameterCollection(); parameters.AddOptional("symbol", symbol); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/openOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/openOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -202,7 +211,8 @@ public async Task>> GetOrdersAsync( parameters.AddOptional("pageIndex", page); parameters.AddOptional("pageSize", pageSize); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/historyOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, 10, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/historyOrders", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(10, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Orders); } @@ -224,7 +234,8 @@ public async Task>> GetUserTradesAsync parameters.AddOptional("fromId", fromId); parameters.AddOptional("limit", limit); - var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/myTrades", BingXExchange.RateLimiter.RestAccount1, 1, true, 5, TimeSpan.FromSeconds(1)); + var request = _definitions.GetOrCreate(HttpMethod.Get, "/openApi/spot/v1/trade/myTrades", BingXExchange.RateLimiter.RestAccount1, 1, true, + limitGuard: new SingleLimitGuard(5, TimeSpan.FromSeconds(1), RateLimitWindowType.Sliding, keySelector: SingleLimitGuard.PerApiKey)); var result = await _baseClient.SendAsync(request, parameters, ct).ConfigureAwait(false); return result.As>(result.Data?.Trades); } diff --git a/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXRestClientPerpetualFuturesApiTrading.cs b/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXRestClientPerpetualFuturesApiTrading.cs index e0f89a0..dcd78b8 100644 --- a/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXRestClientPerpetualFuturesApiTrading.cs +++ b/BingX.Net/Interfaces/Clients/PerpetualFuturesApi/IBingXRestClientPerpetualFuturesApiTrading.cs @@ -35,6 +35,16 @@ public interface IBingXRestClientPerpetualFuturesApiTrading /// Reduce only /// Stop price /// Trailing percentage (between 0 and 1) + /// Stop loss order type + /// Stop loss trigger price + /// Stop loss order price + /// Stop loss trigger price type + /// Stop loss stop guaranteed + /// Take profit order type + /// Take profit trigger price + /// Take profit order price + /// Take profit trigger price type + /// Take profit stop guaranteed /// Time in force /// Close the position /// Trigger price @@ -53,17 +63,17 @@ Task> PlaceTestOrderAsync( decimal? stopPrice = null, decimal? priceRate = null, - //TakeProfitStopLossMode? stopLossType = null, - //decimal? stopLossStopPrice = null, - //decimal? stopLossPrice = null, - //TriggerType? stopLossTriggerType = null, - //bool? stopLossStopGuaranteed = null, + TakeProfitStopLossMode? stopLossType = null, + decimal? stopLossStopPrice = null, + decimal? stopLossPrice = null, + TriggerType? stopLossTriggerType = null, + bool? stopLossStopGuaranteed = null, - //TakeProfitStopLossMode? takeProfitType = null, - //decimal? takeProfitStopPrice = null, - //decimal? takeProfitPrice = null, - //TriggerType? takeProfitTriggerType = null, - //bool? takeProfitStopGuaranteed = null, + TakeProfitStopLossMode? takeProfitType = null, + decimal? takeProfitStopPrice = null, + decimal? takeProfitPrice = null, + TriggerType? takeProfitTriggerType = null, + bool? takeProfitStopGuaranteed = null, TimeInForce? timeInForce = null, bool? closePosition = null, @@ -86,6 +96,16 @@ Task> PlaceTestOrderAsync( /// Reduce only /// Stop price /// Trailing percentage (between 0 and 1) + /// Stop loss order type + /// Stop loss trigger price + /// Stop loss order price + /// Stop loss trigger price type + /// Stop loss stop guaranteed + /// Take profit order type + /// Take profit trigger price + /// Take profit order price + /// Take profit trigger price type + /// Take profit stop guaranteed /// Time in force /// Close the position /// Trigger price @@ -104,17 +124,17 @@ Task> PlaceOrderAsync( decimal? stopPrice = null, decimal? priceRate = null, - //TakeProfitStopLossMode? stopLossType = null, - //decimal? stopLossStopPrice = null, - //decimal? stopLossPrice = null, - //TriggerType? stopLossTriggerType = null, - //bool? stopLossStopGuaranteed = null, + TakeProfitStopLossMode? stopLossType = null, + decimal? stopLossStopPrice = null, + decimal? stopLossPrice = null, + TriggerType? stopLossTriggerType = null, + bool? stopLossStopGuaranteed = null, - //TakeProfitStopLossMode? takeProfitType = null, - //decimal? takeProfitStopPrice = null, - //decimal? takeProfitPrice = null, - //TriggerType? takeProfitTriggerType = null, - //bool? takeProfitStopGuaranteed = null, + TakeProfitStopLossMode? takeProfitType = null, + decimal? takeProfitStopPrice = null, + decimal? takeProfitPrice = null, + TriggerType? takeProfitTriggerType = null, + bool? takeProfitStopGuaranteed = null, TimeInForce? timeInForce = null, bool? closePosition = null, diff --git a/BingX.Net/Objects/Models/BingXFuturesPlaceOrderRequest.cs b/BingX.Net/Objects/Models/BingXFuturesPlaceOrderRequest.cs index 08aa7ad..c529169 100644 --- a/BingX.Net/Objects/Models/BingXFuturesPlaceOrderRequest.cs +++ b/BingX.Net/Objects/Models/BingXFuturesPlaceOrderRequest.cs @@ -31,7 +31,7 @@ public record BingXFuturesPlaceOrderRequest /// /// Reduce only /// - [JsonPropertyName("reduceOnly")] + [JsonPropertyName("reduceOnly"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? ReduceOnly { get; set; } /// /// Price @@ -84,25 +84,59 @@ public record BingXFuturesPlaceOrderRequest [JsonPropertyName("stopGuaranteed"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? StopGuaranteed { get; set; } + /// + /// Stop loss order parameters + /// + [JsonIgnore] + public BingXStopOrderRequest? StopLoss { get; set; } + /// + /// Take profit order parameters + /// + [JsonIgnore] + public BingXStopOrderRequest? TakeProfit { get; set; } - // TODO How to pass the takeProfit/stopLoss fields..? - //[JsonPropertyName("takeProfit"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - //public decimal? TakeProfit { get; set; } - - //[JsonPropertyName("takeProfit"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - //public FuturesOrderType? TakeProfit { get; set; } - //[JsonPropertyName("stopLoss"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - //public FuturesOrderType? StopLoss { get; set; } + /// + /// Internal serialization parameter, use StopLoss for setting stop loss parameters + /// + [JsonPropertyName("stopLoss"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? StopLossStr { get; internal set; } + /// + /// Internal serialization parameter, use TakeProfit for setting take profit parameters + /// + [JsonPropertyName("takeProfit"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? TakeProfitStr { get; internal set; } + } - ///// - ///// Stop loss order - ///// - //[JsonPropertyName("stopLoss"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - //public BingXStopOrder? StopLoss { get; set; } - ///// - ///// Take profit order - ///// - //[JsonPropertyName("takeProfit"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - //public BingXStopOrder? TakeProfit { get; set; } + /// + /// Stop order info + /// + public record BingXStopOrderRequest + { + /// + /// Order type + /// + [JsonPropertyName("type")] + public TakeProfitStopLossMode Type { get; set; } + /// + /// Stop price + /// + [JsonPropertyName("stopPrice")] + public decimal StopPrice { get; set; } + /// + /// Price + /// + [JsonPropertyName("price"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public decimal? Price { get; set; } + /// + /// Trigger type + /// + [JsonPropertyName("workingType"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public TriggerType? TriggerType { get; set; } + /// + /// Stop guarenteed + /// + [JsonPropertyName("stopGuaranteed"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public bool? StopGuaranteed { get; set; } } + } From 4dfa5c76f11881db1532aa59ad1b660e2209845f Mon Sep 17 00:00:00 2001 From: JKorf Date: Tue, 2 Jul 2024 20:24:29 +0200 Subject: [PATCH 2/2] Updated reference --- BingX.Net/BingX.Net.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/BingX.Net/BingX.Net.csproj b/BingX.Net/BingX.Net.csproj index 046fd98..e520657 100644 --- a/BingX.Net/BingX.Net.csproj +++ b/BingX.Net/BingX.Net.csproj @@ -48,12 +48,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - \ No newline at end of file