diff --git a/README.md b/README.md index 228c2648..65533224 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ https://t.me/nodebinanceapi Actively maintained, typed, and safe SDK for the Binance REST APIs and Websockets. Supports ESM and CJS out of the box. ### Features -- Spot, Margin, Futures and Delivery API +- Spot, Margin, Futures and Delivery API (including the new algoOrder service) - Demo trading support - Testnet support (deprecated) - Proxy support (REST and WS) diff --git a/src/node-binance-api.ts b/src/node-binance-api.ts index 0fc91e33..17cffe04 100644 --- a/src/node-binance-api.ts +++ b/src/node-binance-api.ts @@ -1187,9 +1187,70 @@ export default class Binance { if (!params.newClientOrderId) { params.newClientOrderId = this.CONTRACT_PREFIX + this.uuid22(); } + // check if it is algoOrder + const conditionalTypes = [ + 'STOP', + 'STOP_MARKET', + 'TAKE_PROFIT', + 'TAKE_PROFIT_MARKET', + 'TRAILING_STOP_MARKET', + ]; + const typeUpperCase = type.toUpperCase(); + if (typeUpperCase && conditionalTypes.includes(typeUpperCase)) { + const algoPayload = { ...params }; + if (!algoPayload.clientAlgoId) { + algoPayload.clientAlgoId = this.CONTRACT_PREFIX + this.uuid22(); + } + delete algoPayload.newClientOrderId; + algoPayload.algoType = 'CONDITIONAL'; + if (algoPayload.stopPrice && !algoPayload.triggerPrice) { + algoPayload.triggerPrice = algoPayload.stopPrice; + delete algoPayload.stopPrice; + } + return await this.privateFuturesRequest('v1/algoOrder', algoPayload, 'POST'); + } return await this.privateFuturesRequest('v1/order', params, 'POST'); } + // Futures internal functions + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Algo-Order + * @param type + * @param side + * @param symbol symbol if the market + * @param quantity + * @param price + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresAlgoOrder(type: OrderType, side: string, symbol: string, quantity: number, price?: number, params: Dict = {}): Promise { + params.symbol = symbol; + params.side = side; + params.type = type; + if (quantity) params.quantity = quantity; + // if in the binance futures setting Hedged mode is active, positionSide parameter is mandatory + if (!params.positionSide && this.Options.hedgeMode) { + params.positionSide = side === 'BUY' ? 'LONG' : 'SHORT'; + } + // LIMIT STOP MARKET STOP_MARKET TAKE_PROFIT TAKE_PROFIT_MARKET + // reduceOnly stopPrice + if (price) { + params.price = price; + } + if (!params.timeInForce && (params.type.includes('LIMIT') || params.type === 'STOP' || params.type === 'TAKE_PROFIT')) { + params.timeInForce = 'GTX'; // Post only by default. Use GTC for limit orders. + } + + if (!params.clientAlgoId) { + params.clientAlgoId = this.CONTRACT_PREFIX + this.uuid22(); + } + + if (!params.algoType) { + params.algoType = 'CONDITIONAL'; + } + return await this.privateFuturesRequest('v1/algoOrder', params, 'POST'); + } + /** * @see https://developers.binance.com/docs/derivatives/option/trade/New-Order * @param type type of order to create @@ -4502,39 +4563,92 @@ export default class Binance { /** * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Order + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order * @param symbol symbol if the market * @param params extra parameters to be sent in the request + * @param params.conditional set to true to query algo order * @returns */ async futuresOrderStatus(symbol: string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent params.symbol = symbol; + if ('conditional' in params) { + delete params.conditional; + return await this.privateFuturesRequest('v1/algoOrder', params); + } return await this.privateFuturesRequest('v1/order', params); } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-Algo-Order + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresAlgoOrderStatus(symbol: string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + return await this.privateFuturesRequest('v1/algoOrder', params); + } + /** * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Order + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Algo-Order * @param symbol symbol if the market * @param orderId + * @param params.conditional set to true to cancel algo order * @param params extra parameters to be sent in the request * @returns */ async futuresCancel(symbol: string, orderId?: number | string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent params.symbol = symbol; + if ('conditional' in params) { + delete params.conditional; + if (orderId) params.algoid = orderId; + return await this.privateFuturesRequest('v1/algoOrder', params, 'DELETE'); + } if (orderId) params.orderId = orderId; return await this.privateFuturesRequest('v1/order', params, 'DELETE'); } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-Algo-Order + * @param symbol symbol if the market + * @param orderId + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCancelAlgoOrder(symbol: string, orderId?: number | string, params: Dict = {}): Promise { // Either orderId or origClientOrderId must be sent + params.symbol = symbol; + if (orderId) params.algoid = orderId; + return await this.privateFuturesRequest('v1/algoOrder', params, 'DELETE'); + } + /** * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Open-Orders * @param symbol symbol if the market * @param params extra parameters to be sent in the request + * @param params.conditional set to true to cancel algo order * @returns */ async futuresCancelAll(symbol: string, params: Dict = {}): Promise { params.symbol = symbol; + if ('conditional' in params) { + delete params.conditional; + return await this.privateFuturesRequest('v1/algoOpenOrders', params, 'DELETE'); + } return await this.privateFuturesRequest('v1/allOpenOrders', params, 'DELETE'); } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Cancel-All-Algo-Open-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresCancelAllAlgo(symbol: string, params: Dict = {}): Promise { + params.symbol = symbol; + return await this.privateFuturesRequest('v1/algoOpenOrders', params, 'DELETE'); + } + /** * * @param symbol symbol if the market @@ -4552,13 +4666,29 @@ export default class Binance { * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Open-Orders * @param symbol symbol if the market * @param params extra parameters to be sent in the request + * @param params.conditional set to true to query algo open orders * @returns */ async futuresOpenOrders(symbol?: string, params: Dict = {}): Promise { if (symbol) params.symbol = symbol; + if ('conditional' in params) { + delete params.conditional; + return await this.privateFuturesRequest('v1/algoOpenOrders', params); + } return await this.privateFuturesRequest('v1/openOrders', params); } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Current-All-Algo-Open-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresOpenAlgoOrders(symbol?: string, params: Dict = {}): Promise { + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/openAlgoOrders', params); + } + /** * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/All-Orders * @param symbol symbol if the market @@ -4570,6 +4700,17 @@ export default class Binance { return await this.privateFuturesRequest('v1/allOrders', params); } + /** + * @see https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/Query-All-Algo-Orders + * @param symbol symbol if the market + * @param params extra parameters to be sent in the request + * @returns + */ + async futuresAllAlgoOrders(symbol?: string, params: Dict = {}): Promise { // Get all account orders; active, canceled, or filled. + if (symbol) params.symbol = symbol; + return await this.privateFuturesRequest('v1/allAlgoOrders', params); + } + /** * * @param params extra parameters to be sent in the request diff --git a/src/types.ts b/src/types.ts index d858db30..b3c12367 100644 --- a/src/types.ts +++ b/src/types.ts @@ -96,28 +96,42 @@ export interface Order { } export interface FuturesOrder { - clientOrderId: string - cumQty: string - cumQuote: string - executedQty: string - orderId: number - avgPrice: string - origQty: string + clientOrderId?: string + cumQty?: string + cumQuote?: string + executedQty?: string + orderId?: number + avgPrice?: string + origQty?: string price: string reduceOnly: boolean side: OrderSide positionSide: PositionSide - status: OrderStatus - stopPrice: string - closePosition: boolean + status?: OrderStatus + stopPrice?: string + closePosition?: boolean symbol: string - timeInForce: TimeInForce - type: OrderType - origType: OrderType - activatePrice: string - priceRate: string - updateTime: number - workingType: WorkingType + timeInForce?: TimeInForce + type?: OrderType + origType?: OrderType + activatePrice?: string + priceRate?: string + updateTime?: number + workingType?: WorkingType + // algo orders fields + algoId?: number; + triggerPrice?: string; + orderStatus?: string; + clientAlgoId?: string; + algoStatus?: string; + actualOrderId?: string; + actualPrice?: string; + tpTriggerPrice?: string; + tpPrice?: string; + slTriggerPrice?: string; + slPrice?: string; + tpOrderType?: string; + slOrderType?: string; } export type PositionSide = 'BOTH' | 'SHORT' | 'LONG' diff --git a/tests/binance-class-static.test.ts b/tests/binance-class-static.test.ts index 1a6fbc62..de8a847f 100644 --- a/tests/binance-class-static.test.ts +++ b/tests/binance-class-static.test.ts @@ -235,6 +235,30 @@ describe( 'Static tests', async function () { assert(obj.newClientOrderId.startsWith(CONTRACT_PREFIX)) }) + it( 'Futures Limit trigger Buy', async function ( ) { + await binance.futuresOrder('STOP', 'BUY', 'LTCUSDT', 0.5, 100, {'triggerPrice': 90} ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/algoOrder' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/algoOrder?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'STOP' ) + assert.equal( obj.algoType, 'CONDITIONAL') + assert.equal( obj.quantity, 0.5 ) + assert(obj.clientAlgoId.startsWith(CONTRACT_PREFIX)) + }) + + it( 'Futures Limit trigger Buy using dedicated endpoint', async function ( ) { + await binance.futuresAlgoOrder('STOP', 'BUY', 'LTCUSDT', 0.5, 100, {'triggerPrice': 90} ) + assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/algoOrder' )) + const obj = urlToObject( interceptedUrl.replace('https://fapi.binance.com/fapi/v1/algoOrder?', '') ) + assert.equal( obj.symbol, 'LTCUSDT' ) + assert.equal( obj.side, 'BUY' ) + assert.equal( obj.type, 'STOP' ) + assert.equal( obj.algoType, 'CONDITIONAL') + assert.equal( obj.quantity, 0.5 ) + assert(obj.clientAlgoId.startsWith(CONTRACT_PREFIX)) + }) + it( 'Futures LimitSell', async function ( ) { await binance.futuresOrder('LIMIT', 'SELL', 'LTCUSDT', 0.5, 100 ) assert.isTrue( interceptedUrl.startsWith('https://fapi.binance.com/fapi/v1/order' ))