Skip to content

Commit 18f33cb

Browse files
committed
Add AmendOrderAsync method to BinanceWebsocketClientSpotApiTrading
Reduce the quantity of an existing open order See https://developers.binance.com/docs/binance-spot-api-docs/websocket-api/trading-requests#order-amend-keep-priority-trade
1 parent f6331af commit 18f33cb

7 files changed

Lines changed: 175 additions & 38 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
PUT
2+
/api/v3/order/amend/keepPriority
3+
true
4+
{
5+
"transactTime": 1684804350068,
6+
"executionId": 1234567890123456789,
7+
"amendedOrder": {
8+
"symbol": "BTCUSDT",
9+
"orderId": 12569099453,
10+
"orderListId": -1,
11+
"clientOrderId": "4d96324ff9d44481926157",
12+
"price": "23416.10000000",
13+
"origQty": "0.00847000",
14+
"executedQty": "0.00847000",
15+
"cummulativeQuoteQty": "198.33521500",
16+
"status": "NEW",
17+
"timeInForce": "GTC",
18+
"type": "LIMIT",
19+
"side": "SELL",
20+
"stopPrice": "0.00000000",
21+
"trailingDelta": 10,
22+
"trailingTime": -1,
23+
"icebergQty": "0.00000000",
24+
"time": 1660801715639,
25+
"updateTime": 1660801717945,
26+
"isWorking": true,
27+
"workingTime": 1660801715639,
28+
"origQuoteOrderQty": "0.00000000",
29+
"strategyId": 37463720,
30+
"strategyType": 1000000,
31+
"selfTradePreventionMode": "NONE",
32+
"preventedMatchId": 0,
33+
"preventedQuantity": "1.200000"
34+
}
35+
}

Binance.Net.UnitTests/RestRequestTests.cs

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public async Task ValidateSpotTradingCalls()
160160
await tester.ValidateAsync(client => client.SpotApi.Trading.CancelOrderAsync("ETHUSDT", 123), "CancelOrder");
161161
await tester.ValidateAsync(client => client.SpotApi.Trading.CancelAllOrdersAsync("ETHUSDT"), "CancelAllOrders");
162162
await tester.ValidateAsync(client => client.SpotApi.Trading.ReplaceOrderAsync("ETHUSDT", Enums.OrderSide.Sell, Enums.SpotOrderType.Limit, Enums.CancelReplaceMode.AllowFailure, 123, quantity: 1), "ReplaceOrder", nestedJsonProperty: "data");
163-
await tester.ValidateAsync(client => client.SpotApi.Trading.AmendOrderAsync("ETHUSDT", 123, newQty: 0.5), "AmendOrder");
163+
await tester.ValidateAsync(client => client.SpotApi.Trading.AmendOrderAsync("ETHUSDT", newQuantity: 5.0m, orderId: 123), "AmendOrder");
164164
await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrderAsync("ETHUSDT", 123), "GetOrder");
165165
await tester.ValidateAsync(client => client.SpotApi.Trading.GetOpenOrdersAsync("ETHUSDT"), "GetOpenOrders");
166166
await tester.ValidateAsync(client => client.SpotApi.Trading.GetOrdersAsync("ETHUSDT"), "GetOrders");
@@ -626,30 +626,30 @@ public async Task ValidateGeneralSimpleEarnCalls()
626626
RateLimiterEnabled = false,
627627
}));
628628
var tester = new RestRequestValidator<BinanceRestClient>(client, "Endpoints/General/SimpleEarn", "https://api.binance.com", IsAuthenticated);
629-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetAccountAsync(),"GetAccount");
630-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleProductsAsync(),"GetFlexibleProducts");
631-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedProductsAsync(),"GetLockedProducts");
632-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleProductPositionsAsync(),"GetFlexibleProductPositions");
633-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedProductPositionsAsync(),"GetLockedProductPositions");
634-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexiblePersonalQuotaLeftAsync("123"),"GetFlexiblePersonalQuotaLeft");
635-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedPersonalQuotaLeftAsync("123"),"GetLockedPersonalQuotaLeft");
636-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SubscribeFlexibleProductAsync("123", 1),"SubscribeFlexibleProduct");
637-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SubscribeLockedProductAsync("123", 1),"SubscribeLockedProduct");
638-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.RedeemFlexibleProductAsync("123"),"RedeemFlexibleProduct");
639-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.RedeemLockedProductAsync("123"),"RedeemLockedProduct");
640-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetFlexibleAutoSubscribeAsync("123", true),"SetFlexibleAutoSubscribe");
641-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetLockedAutoSubscribeAsync("123", true),"SetLockedAutoSubscribe");
642-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleSubscriptionPreviewAsync("123", 1),"GetFlexibleSubscriptionPreview");
643-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedSubscriptionPreviewAsync("123", 1),"GetLockedSubscriptionPreview");
644-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetLockedRedeemOptionAsync("123", RedeemDestination.Flexible),"SetLockedRedeemOption");
645-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleSubscriptionRecordsAsync(),"GetFlexibleSubscriptionRecords");
646-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedSubscriptionRecordsAsync(),"GetLockedSubscriptionRecords");
647-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleRedemptionRecordsAsync(),"GetFlexibleRedemptionRecords");
648-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedRedemptionRecordsAsync(),"GetLockedRedemptionRecords");
649-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleRewardRecordsAsync(RewardType.All),"GetFlexibleRewardRecords");
650-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedRewardRecordsAsync(),"GetLockedRewardRecords");
651-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetCollateralRecordsAsync("123"),"GetCollateralRecords");
652-
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetRateHistoryAsync("123"),"GetRateHistory");
629+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetAccountAsync(), "GetAccount");
630+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleProductsAsync(), "GetFlexibleProducts");
631+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedProductsAsync(), "GetLockedProducts");
632+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleProductPositionsAsync(), "GetFlexibleProductPositions");
633+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedProductPositionsAsync(), "GetLockedProductPositions");
634+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexiblePersonalQuotaLeftAsync("123"), "GetFlexiblePersonalQuotaLeft");
635+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedPersonalQuotaLeftAsync("123"), "GetLockedPersonalQuotaLeft");
636+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SubscribeFlexibleProductAsync("123", 1), "SubscribeFlexibleProduct");
637+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SubscribeLockedProductAsync("123", 1), "SubscribeLockedProduct");
638+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.RedeemFlexibleProductAsync("123"), "RedeemFlexibleProduct");
639+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.RedeemLockedProductAsync("123"), "RedeemLockedProduct");
640+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetFlexibleAutoSubscribeAsync("123", true), "SetFlexibleAutoSubscribe");
641+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetLockedAutoSubscribeAsync("123", true), "SetLockedAutoSubscribe");
642+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleSubscriptionPreviewAsync("123", 1), "GetFlexibleSubscriptionPreview");
643+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedSubscriptionPreviewAsync("123", 1), "GetLockedSubscriptionPreview");
644+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.SetLockedRedeemOptionAsync("123", RedeemDestination.Flexible), "SetLockedRedeemOption");
645+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleSubscriptionRecordsAsync(), "GetFlexibleSubscriptionRecords");
646+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedSubscriptionRecordsAsync(), "GetLockedSubscriptionRecords");
647+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleRedemptionRecordsAsync(), "GetFlexibleRedemptionRecords");
648+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedRedemptionRecordsAsync(), "GetLockedRedemptionRecords");
649+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetFlexibleRewardRecordsAsync(RewardType.All), "GetFlexibleRewardRecords");
650+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetLockedRewardRecordsAsync(), "GetLockedRewardRecords");
651+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetCollateralRecordsAsync("123"), "GetCollateralRecords");
652+
await tester.ValidateAsync(client => client.GeneralApi.SimpleEarn.GetRateHistoryAsync("123"), "GetRateHistory");
653653
}
654654

655655
[Test]
@@ -666,19 +666,19 @@ public async Task ValidateGeneralMiningCalls()
666666
RateLimiterEnabled = false,
667667
}));
668668
var tester = new RestRequestValidator<BinanceRestClient>(client, "Endpoints/General/Mining", "https://api.binance.com", IsAuthenticated);
669-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningCoinListAsync(),"GetMiningCoinList", nestedJsonProperty: "data");
670-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAlgorithmListAsync(),"GetMiningAlgorithmList", nestedJsonProperty: "data");
671-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMinerDetailsAsync("sha256", "test", "bhdc1.16A10404B"),"GetMinerDetails", nestedJsonProperty: "data");
672-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMinerListAsync("sha256", "test"),"GetMinerList", nestedJsonProperty: "data");
673-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningRevenueListAsync("sha256", "test"),"GetMiningRevenueList", nestedJsonProperty: "data");
674-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningOtherRevenueListAsync("sha256", "test"),"GetMiningOtherRevenueList", nestedJsonProperty: "data");
675-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningStatisticsAsync("sha256", "test"),"GetMiningStatistics", nestedJsonProperty: "data");
676-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAccountListAsync("sha256", "test"),"GetMiningAccountList", nestedJsonProperty: "data");
677-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetHashrateResaleListAsync(),"GetHashrateResaleList", nestedJsonProperty: "data");
678-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetHashrateResaleDetailsAsync(168, "test"),"GetHashrateResaleDetails", nestedJsonProperty: "data");
679-
await tester.ValidateAsync(client => client.GeneralApi.Mining.PlaceHashrateResaleRequestAsync("test", "sha256", DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, "S19Pro", 100000000),"PlaceHashrateResaleRequest", nestedJsonProperty: "data");
680-
await tester.ValidateAsync(client => client.GeneralApi.Mining.CancelHashrateResaleRequestAsync(168, "test"),"CancelHashrateResaleRequest", nestedJsonProperty: "data");
681-
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAccountEarningsAsync("sha256"),"GetMiningAccountEarnings", nestedJsonProperty: "data");
669+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningCoinListAsync(), "GetMiningCoinList", nestedJsonProperty: "data");
670+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAlgorithmListAsync(), "GetMiningAlgorithmList", nestedJsonProperty: "data");
671+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMinerDetailsAsync("sha256", "test", "bhdc1.16A10404B"), "GetMinerDetails", nestedJsonProperty: "data");
672+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMinerListAsync("sha256", "test"), "GetMinerList", nestedJsonProperty: "data");
673+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningRevenueListAsync("sha256", "test"), "GetMiningRevenueList", nestedJsonProperty: "data");
674+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningOtherRevenueListAsync("sha256", "test"), "GetMiningOtherRevenueList", nestedJsonProperty: "data");
675+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningStatisticsAsync("sha256", "test"), "GetMiningStatistics", nestedJsonProperty: "data");
676+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAccountListAsync("sha256", "test"), "GetMiningAccountList", nestedJsonProperty: "data");
677+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetHashrateResaleListAsync(), "GetHashrateResaleList", nestedJsonProperty: "data");
678+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetHashrateResaleDetailsAsync(168, "test"), "GetHashrateResaleDetails", nestedJsonProperty: "data");
679+
await tester.ValidateAsync(client => client.GeneralApi.Mining.PlaceHashrateResaleRequestAsync("test", "sha256", DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, "S19Pro", 100000000), "PlaceHashrateResaleRequest", nestedJsonProperty: "data");
680+
await tester.ValidateAsync(client => client.GeneralApi.Mining.CancelHashrateResaleRequestAsync(168, "test"), "CancelHashrateResaleRequest", nestedJsonProperty: "data");
681+
await tester.ValidateAsync(client => client.GeneralApi.Mining.GetMiningAccountEarningsAsync("sha256"), "GetMiningAccountEarnings", nestedJsonProperty: "data");
682682
}
683683

684684
// Staking tests (placeholder)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
> { "id": "|1|", "method": "order.amend.keepPriority", "params": { "apiKey": "123", "signature": "-", "timestamp": 1660801839480 } }
2+
=
3+
{
4+
"id": "|1|",
5+
"status": 200,
6+
"result": {
7+
"transactTime": 1660801715639,
8+
"executionId": 12569099453,
9+
"amendedOrder": {
10+
"symbol": "BTCUSDT",
11+
"orderId": 12569099453,
12+
"orderListId": -1,
13+
"clientOrderId": "4d96324ff9d44481926157",
14+
"price": "23416.10000000",
15+
"origQty": "0.00847000",
16+
"executedQty": "0.00847000",
17+
"cummulativeQuoteQty": "198.33521500",
18+
"status": "NEW",
19+
"timeInForce": "GTC",
20+
"type": "LIMIT",
21+
"side": "SELL",
22+
"stopPrice": "0.00000000",
23+
"trailingDelta": 10,
24+
"trailingTime": -1,
25+
"icebergQty": "0.00000000",
26+
"time": 1660801715639,
27+
"updateTime": 1660801717945,
28+
"isWorking": true,
29+
"workingTime": 1660801715639,
30+
"origQuoteOrderQty": "0.00000000",
31+
"strategyId": 37463720,
32+
"strategyType": 1000000,
33+
"selfTradePreventionMode": "NONE",
34+
"preventedMatchId": 0,
35+
"preventedQuantity": "1.200000"
36+
}
37+
},
38+
"rateLimits": [
39+
{
40+
"rateLimitType": "REQUEST_WEIGHT",
41+
"interval": "MINUTE",
42+
"intervalNum": 1,
43+
"limit": 6000,
44+
"count": 4
45+
}
46+
]
47+
}

Binance.Net.UnitTests/SocketRequestTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public async Task ValidateSpotTradingCalls()
100100
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.GetOrderAsync("ETHUSDT", 123), "GetOrder", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
101101
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.CancelOrderAsync("ETHUSDT", 123), "CancelOrder", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
102102
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.ReplaceOrderAsync("ETHUSDT", Enums.OrderSide.Buy, Enums.SpotOrderType.Limit, Enums.CancelReplaceMode.AllowFailure, 123), "ReplaceOrder", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
103+
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.AmendOrderAsync("ETHUSDT", newQuantity: 5.05m, orderId: 123), "AmendOrder", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
103104
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.GetOpenOrdersAsync("ETHUSDT"), "GetOpenOrders", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
104105
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.CancelAllOrdersAsync("ETHUSDT"), "CancelAllOrders", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });
105106
await tester.ValidateAsync(CreateClient(), client => client.SpotApi.Trading.PlaceOcoOrderListAsync("ETHUSDT", Enums.OrderSide.Buy, 1, Enums.SpotOrderType.Limit, Enums.SpotOrderType.Limit), "PlaceOcoOrder", responseMapper: x => x.Result, nestedJsonProperty: "result", ignoreProperties: new List<string> { "" });

Binance.Net/Clients/SpotApi/BinanceSocketClientSpotApiTrading.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,45 @@ public async Task<CallResult<BinanceResponse<BinanceReplaceOrderResult>>> Replac
269269

270270
#endregion
271271

272+
#region Amend Order
273+
274+
/// <inheritdoc />
275+
public async Task<CallResult<BinanceResponse<BinanceAmendedOrderResult>>> AmendOrderAsync(string symbol, decimal newQuantity, long? orderId = null, string? clientOrderId = null, string? newClientOrderId = null, CancellationToken ct = default)
276+
{
277+
if (!orderId.HasValue && string.IsNullOrEmpty(clientOrderId))
278+
return new CallResult<BinanceResponse<BinanceAmendedOrderResult>>(ArgumentError.Invalid("Either orderId or clientOrderId must be provided", "Either orderId or clientOrderId must be provided"));
279+
280+
if (newClientOrderId != null)
281+
{
282+
newClientOrderId = LibraryHelpers.ApplyBrokerId(
283+
newClientOrderId,
284+
LibraryHelpers.GetClientReference(() => _client.ClientOptions.BrokerId, _client.Exchange, "Spot"),
285+
36,
286+
_client.ClientOptions.AllowAppendingClientOrderId);
287+
}
288+
289+
if (clientOrderId != null)
290+
{
291+
clientOrderId = LibraryHelpers.ApplyBrokerId(
292+
clientOrderId,
293+
LibraryHelpers.GetClientReference(() => _client.ClientOptions.BrokerId, _client.Exchange, "Spot"),
294+
36,
295+
_client.ClientOptions.AllowAppendingClientOrderId);
296+
}
297+
298+
var parameters = new ParameterCollection
299+
{
300+
{ "symbol", symbol },
301+
{ "newQty", newQuantity.ToString(CultureInfo.InvariantCulture) }
302+
};
303+
parameters.AddOptionalParameter("orderId", orderId?.ToString(CultureInfo.InvariantCulture));
304+
parameters.AddOptionalParameter("origClientOrderId", clientOrderId);
305+
parameters.AddOptionalParameter("newClientOrderId", newClientOrderId);
306+
return await _client.QueryAsync<BinanceAmendedOrderResult>(_client.ClientOptions.Environment.SpotSocketApiAddress.AppendPath("ws-api/v3"), $"order.amend.keepPriority", parameters, true, true, weight: 4, ct: ct).ConfigureAwait(false);
307+
}
308+
309+
#endregion
310+
272311
#region Get Open Orders
273312

274313
/// <inheritdoc />

0 commit comments

Comments
 (0)