Skip to content
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ Following examples will use the `await` form, which requires some configuration
- [delivery markPrice](#delivery-markprice)
- [Authenticated REST Endpoints](#authenticated-rest-endpoints)
- [order](#order)
- [updateOrder](#updateorder)
- [orderTest](#ordertest)
- [orderOco](#orderoco)
- [getOrder](#getorder)
Expand Down Expand Up @@ -207,6 +208,8 @@ Following examples will use the `await` form, which requires some configuration
- [Portfolio Margin Endpoints](#portfolio-margin-endpoints)
- [getPortfolioMarginAccountInfo](#getportfoliomarginaccountinfo)
- [Futures Authenticated REST endpoints](#futures-authenticated-rest-endpoints)
- [futuresOrder](#futuresorder)
- [futuresUpdateOrder](#futuresupdateorder)
- [futuresGetOrder](#futuresgetorder)
- [futuresAllOrders](#futuresallorders)
- [futuresBatchOrders](#futuresbatchorders)
Expand Down Expand Up @@ -1377,7 +1380,8 @@ the request.

#### order

Creates a new order.
- Creates a new order.
- see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade

```js
console.log(
Expand Down Expand Up @@ -1453,6 +1457,26 @@ Test new order creation and signature/recvWindow. Creates and validates a new or

Same API as above, but does not return any output on success.


#### updateOrder

- updates an order
- see https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#cancel-an-existing-order-and-send-a-new-order-trade

```Js

const order = await client.updateOrder({
symbol: 'LTCUSDT',
cancelOrderId: 12345678,
side: 'BUY',
type: 'LIMIT',
quantity: 1,
price: 80,
timeInForce: 'GTC',
})

```

#### orderOco

Creates a new OCO order.
Expand Down Expand Up @@ -3617,6 +3641,42 @@ console.log(await client.getPortfolioMarginAccountInfo())

### Futures Authenticated REST endpoints

#### futuresOrder

- Creates a futures order
- see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api

```js
console.log(
await client.futuresOrder({
symbol: 'LTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: 1,
price: 80,
timeInForce: 'GTC',
})
)
```

#### futuresUpdateOrder
- Updates a futures order
- see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Modify-Order

```js
console.log(
await client.futuresUpdateOrder({
orderId: 23423423423,
symbol: 'LTCUSDT',
side: 'BUY',
type: 'LIMIT',
quantity: 1,
price: 80,
timeInForce: 'GTC',
})
)
```

#### futuresGetOrder

Check an order's status.
Expand Down
31 changes: 31 additions & 0 deletions src/http-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,27 @@ const orderOco = (privCall, payload = {}, url) => {
)
}

const updateOrder = (privCall, payload = {}, url) => {
const newPayload = { ...payload }

if (!newPayload.cancelReplaceMode) {
newPayload.cancelReplaceMode = 'STOP_ON_FAILURE'
}

if (!newPayload.timeInForce) {
newPayload.timeInForce = 'GTC'
}

if (!newPayload.newClientOrderId) {
newPayload.newClientOrderId = spotP()
}

return (
checkParams('updateOrder', newPayload, ['symbol', 'side', 'type']) &&
privCall(url, newPayload, 'POST')
)
}

/**
* Zip asks and bids reponse from order book
*/
Expand Down Expand Up @@ -486,6 +507,7 @@ export default opts => {

// Order endpoints
order: payload => order(privCall, payload, '/api/v3/order'),
updateOrder: payload => updateOrder(privCall, payload, '/api/v3/order/cancelReplace'),
orderOco: payload => orderOco(privCall, payload, '/api/v3/order/oco'),
orderTest: payload => order(privCall, payload, '/api/v3/order/test'),
getOrder: payload => privCall('/api/v3/order', payload),
Expand Down Expand Up @@ -598,6 +620,15 @@ export default opts => {
// Use regular order endpoint
return order(privCall, payload, '/fapi/v1/order')
},
futuresUpdateOrder: payload => {
if (payload && 'conditional' in payload) {
// for now it is not supported
// const payloadCopy = { ...payload }
// delete payloadCopy.conditional
// return privCall('/fapi/v1/algoOrder', payloadCopy, 'PUT')
Comment on lines +625 to +628
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out code should be removed. If this functionality is planned for future implementation, consider creating a TODO comment or GitHub issue instead of leaving commented code in the codebase.

Copilot uses AI. Check for mistakes.
}
Comment on lines +623 to +629
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional check at line 603 does not perform any action when 'conditional' is present in the payload. The if block is empty (only contains commented code), so the function will continue to line 609 regardless. Either implement the conditional order update logic or remove this placeholder code.

Copilot uses AI. Check for mistakes.
return privCall('/fapi/v1/order', payload, 'PUT')
},
Comment on lines +623 to +631
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The futuresUpdateOrder endpoint lacks test coverage. Other futures order endpoints in test/futures.js have corresponding tests (e.g., futuresOrder, futuresGetOrder, futuresCancelOrder). Consider adding tests for futuresUpdateOrder to verify parameter handling, error cases, and successful order updates using the /fapi/v1/order PUT endpoint.

Copilot uses AI. Check for mistakes.
Comment on lines +623 to +631
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The futuresUpdateOrder implementation is inconsistent with futuresGetOrder and futuresCancelOrder patterns. Both of those methods handle conditional orders by checking the conditional flag and properly routing to the algo endpoint, and they also create a payloadCopy to remove the 'conditional' property before making the API call. Consider implementing similar logic here for consistency.

Copilot uses AI. Check for mistakes.
futuresBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'POST'),
futuresGetOrder: payload => {
// Check if this is a request for a conditional/algo order
Expand Down
45 changes: 45 additions & 0 deletions test/static-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ test.serial.beforeEach(t => {
interceptedBody = requestBody
return { success: true }
})
nock(/.*/)
.put(/.*/)
.reply(200, function (uri, requestBody) {
interceptedUrl = `${this.req.options.proto}://${this.req.options.hostname}${uri}`
interceptedBody = requestBody
return { success: true }
})
nock(/.*/)
.delete(/.*/)
.reply(200, function (uri, requestBody) {
Expand Down Expand Up @@ -277,6 +284,24 @@ test.serial('[REST] Futures LimitSell', async t => {
t.true(obj.newClientOrderId.startsWith(CONTRACT_PREFIX))
})

test.serial('[REST] Futures update/edit order', async t => {
await binance.futuresUpdateOrder({
symbol: 'LTCUSDT',
side: 'SELL',
type: 'LIMIT',
quantity: 0.5,
price: 100,
orderId: 1234,
})
t.true(interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order'))
const obj = urlToObject(interceptedUrl.replace('https://fapi.binance.com/fapi/v1/order?', ''))
t.is(obj.symbol, 'LTCUSDT')
t.is(obj.side, 'SELL')
t.is(obj.type, 'LIMIT')
t.is(obj.quantity, '0.5')
t.is(obj.orderId, '1234')
})

test.serial('[REST] Futures cancel order', async t => {
await binance.futuresCancelOrder({ symbol: 'LTCUSDT', orderId: '34234234' })
const url = 'https://fapi.binance.com/fapi/v1/order'
Expand All @@ -298,6 +323,26 @@ test.serial('[REST] MarketBuy test', async t => {
t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX))
})

test.serial('[REST] update spot order', async t => {
await binance.updateOrder({
symbol: 'LTCUSDT',
side: 'BUY',
type: 'MARKET',
quantity: 0.5,
cancelOrderId: 1234,
})
t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/order/cancelReplace'))
const body = interceptedUrl.replace('https://api.binance.com/api/v3/order/cancelReplace', '')
const obj = urlToObject(body)
t.is(obj.symbol, 'LTCUSDT')
t.is(obj.side, 'BUY')
t.is(obj.type, 'MARKET')
t.is(obj.quantity, '0.5')
t.is(obj.cancelReplaceMode, 'STOP_ON_FAILURE')
t.is(obj.cancelOrderId, '1234')
t.true(obj.newClientOrderId.startsWith(SPOT_PREFIX))
})

test.serial('[REST] spot open orders', async t => {
await binance.openOrders({ symbol: 'LTCUSDT' })
t.true(interceptedUrl.startsWith('https://api.binance.com/api/v3/openOrders'))
Expand Down
40 changes: 40 additions & 0 deletions types/futures.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,46 @@ export interface FuturesEndpoints extends BinanceRestClient {
commissionAsset: string;
}>;
}>;
futuresUpdateOrder(payload: {
orderId?: number;
origClientOrderId?: string;
symbol: string;
side: OrderSide;
type: OrderType;
quantity?: string;
price?: string;
priceMatch?: string;
}): Promise<{
symbol: string;
orderId: number;
orderListId: number;
clientOrderId: string;
transactTime: number;
price: string;
origQty: string;
executedQty: string;
cumQuote: string;
status: OrderStatus;
timeInForce: TimeInForce;
type: OrderType;
side: OrderSide;
marginBuyBorrowAmount: string;
marginBuyBorrowAsset: string;
stopPrice?: string;
workingType?: string;
priceProtect?: string;
origType?: string;
priceMatch?: string;
selfTradePreventionMode?: string;
goodTillDate?: number;
updateTime?: number;
fills: Array<{
price: string;
qty: string;
commission: string;
commissionAsset: string;
}>;
}>;
futuresBatchOrders(payload: { batchOrders: Array<{
symbol: string;
side: OrderSide;
Expand Down
70 changes: 70 additions & 0 deletions types/order.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,76 @@ export interface OrderEndpoints extends BinanceRestClient {
commissionAsset: string;
}>;
}>;
updateOrder(payload: {
symbol: string;
side: OrderSide;
type: OrderType;
quantity?: string;
cancelReplaceMode?: string;
price?: string;
cancelNewClientOrderId?: string;
quoteOrderQty?: string;
cancelOrigClientOrderId?: string;
cancelOrderId?: number;
newClientOrderId?: string;
timeInForce?: TimeInForce;
strategyId?: number;
strategyType?: number;
stopPrice?: string;
trailingDelta?: number;
trailingTime?: number;
icebergQty?: string;
newOrderRespType?: string;
selfTradePreventionMode?: string;
cancelRestrictions?: string;
orderRateLimitExceededMode?: string;
pegPriceType?: string;
pegOffsetValue?: string;
pegOffsetType?: string;
}): Promise<{
cancelResult: string;
newOrderResult: string;
cancelResponse: {
symbol: string;
origClientOrderId: string;
orderId: number;
orderListId: number;
clientOrderId: string;
transactTime: number;
price: string;
origQty: string;
executedQty: string;
cummulativeQuoteQty: string;
status: OrderStatus;
timeInForce: TimeInForce;
type: OrderType;
side: OrderSide;
};
newOrderResponse: {
symbol: string;
orderId: number;
orderListId: number;
clientOrderId: string;
transactTime: number;
price: string;
origQty: string;
executedQty: string;
origQuoteOrderQty: string;
cummulativeQuoteQty: string;
status: OrderStatus;
timeInForce: TimeInForce;
type: OrderType;
side: OrderSide;
workingTime: number;
fills: Array<{
price: string;
qty: string;
commission: string;
commissionAsset: string;
}>;
selfTradePreventionMode: string;
};
}>;
orderOco(payload: {
symbol: string;
side: OrderSide;
Expand Down