diff --git a/README.md b/README.md index be0852b6..6adcd38e 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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) @@ -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( @@ -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. @@ -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. diff --git a/src/http-client.js b/src/http-client.js index 39f7e74a..7b5d6130 100644 --- a/src/http-client.js +++ b/src/http-client.js @@ -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 */ @@ -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), @@ -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') + } + return privCall('/fapi/v1/order', payload, 'PUT') + }, futuresBatchOrders: payload => privCall('/fapi/v1/batchOrders', payload, 'POST'), futuresGetOrder: payload => { // Check if this is a request for a conditional/algo order diff --git a/test/static-tests.js b/test/static-tests.js index 417de2a4..811511e8 100644 --- a/test/static-tests.js +++ b/test/static-tests.js @@ -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) { @@ -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' @@ -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')) diff --git a/types/futures.d.ts b/types/futures.d.ts index 1d57e6d7..2789e64a 100644 --- a/types/futures.d.ts +++ b/types/futures.d.ts @@ -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; diff --git a/types/order.d.ts b/types/order.d.ts index c7c40165..29a1713d 100644 --- a/types/order.d.ts +++ b/types/order.d.ts @@ -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;