diff --git a/.prettierrc b/.prettierrc index 1fb73bc..8d6ce6a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { "tabWidth": 2, - "singleQuote": true + "singleQuote": true, + "trailingComma": "all" } diff --git a/examples/rest-trade-futures.ts b/examples/rest-trade-futures.ts index 43dc87a..58ca502 100644 --- a/examples/rest-trade-futures.ts +++ b/examples/rest-trade-futures.ts @@ -47,7 +47,7 @@ function promiseSleep(milliseconds) { // WARNING: for sensitive math you should be using a library such as decimal.js! function roundDown(value, decimals) { return Number( - Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals + Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals, ); } @@ -117,7 +117,7 @@ async function handleWsUpdate(event) { const symbolRulesResult = await client.getSymbols('umcbl'); const bitcoinUSDFuturesRule = symbolRulesResult.data.find( - (row) => row.symbol === symbol + (row) => row.symbol === symbol, ); console.log('symbol rules: ', bitcoinUSDFuturesRule); @@ -142,7 +142,7 @@ async function handleWsUpdate(event) { const positionsResult = await client.getPositions('umcbl'); const positionsToClose = positionsResult.data.filter( - (pos) => pos.total !== '0' + (pos) => pos.total !== '0', ); console.log('open positions to close: ', positionsToClose); @@ -167,7 +167,7 @@ async function handleWsUpdate(event) { console.log( 'positions after closing all: ', - await client.getPositions('umcbl') + await client.getPositions('umcbl'), ); } catch (e) { console.error('request failed: ', e); diff --git a/examples/rest-trade-spot.ts b/examples/rest-trade-spot.ts index 02e3441..a990097 100644 --- a/examples/rest-trade-spot.ts +++ b/examples/rest-trade-spot.ts @@ -35,7 +35,7 @@ function promiseSleep(milliseconds) { // WARNING: for sensitive math you should be using a library such as decimal.js! function roundDown(value, decimals) { return Number( - Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals + Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals, ); } diff --git a/examples/ws-private.ts b/examples/ws-private.ts index a6356c5..fa74781 100644 --- a/examples/ws-private.ts +++ b/examples/ws-private.ts @@ -22,7 +22,7 @@ import { WebsocketClient, DefaultLogger } from '../src'; // optionally provide rest options, e.g. to pass through a proxy // }, }, - logger + logger, ); wsClient.on('update', (data) => { diff --git a/examples/ws-public.ts b/examples/ws-public.ts index 39c40a2..52a8701 100644 --- a/examples/ws-public.ts +++ b/examples/ws-public.ts @@ -15,7 +15,7 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; // optionally provide rest options, e.g. to pass through a proxy // }, }, - logger + logger, ); wsClient.on('update', (data) => { diff --git a/package.json b/package.json index 53dfe5d..15aec87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitget-api", - "version": "1.0.4", + "version": "1.0.5", "description": "Node.js connector for Bitget REST APIs and WebSockets, with TypeScript & end-to-end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/broker-client.ts b/src/broker-client.ts index 5ca8493..d96816b 100644 --- a/src/broker-client.ts +++ b/src/broker-client.ts @@ -30,7 +30,7 @@ export class BrokerClient extends BaseRestClient { /** Create Sub Account */ createSubAccount( subName: string, - remark?: string + remark?: string, ): Promise> { return this.postPrivate('/api/broker/v1/account/sub-create', { subName, @@ -47,7 +47,7 @@ export class BrokerClient extends BaseRestClient { modifySubAccount( subUid: string, perm: string, - status: 'normal' | 'freeze' | 'del' + status: 'normal' | 'freeze' | 'del', ): Promise> { return this.postPrivate('/api/broker/v1/account/sub-modify', { subUid, @@ -79,7 +79,7 @@ export class BrokerClient extends BaseRestClient { /** Get Sub Future Assets */ getSubFutureAssets( subUid: string, - productType: BrokerProductType + productType: BrokerProductType, ): Promise> { return this.getPrivate('/api/broker/v1/account/sub-future-assets', { subUid, @@ -91,7 +91,7 @@ export class BrokerClient extends BaseRestClient { getSubDepositAddress( subUid: string, coin: string, - chain?: string + chain?: string, ): Promise> { return this.postPrivate('/api/broker/v1/account/sub-address', { subUid, @@ -109,7 +109,7 @@ export class BrokerClient extends BaseRestClient { setSubDepositAutoTransfer( subUid: string, coin: string, - toAccountType: 'spot' | 'mix_usdt' | 'mix_usd' | 'mix_usdc' + toAccountType: 'spot' | 'mix_usdt' | 'mix_usd' | 'mix_usdc', ): Promise> { return this.postPrivate('/api/broker/v1/account/sub-auto-transfer', { subUid, @@ -130,7 +130,7 @@ export class BrokerClient extends BaseRestClient { passphrase: string, remark: string, ip: string, - perm?: string + perm?: string, ): Promise> { return this.postPrivate('/api/broker/v1/manage/sub-api-create', { subUid, @@ -148,7 +148,7 @@ export class BrokerClient extends BaseRestClient { /** Modify Sub ApiKey (Only Broker) */ modifySubAPIKey( - params: BrokerSubAPIKeyModifyRequest + params: BrokerSubAPIKeyModifyRequest, ): Promise> { return this.postPrivate('/api/broker/v1/manage/sub-api-modify', params); } diff --git a/src/futures-client.ts b/src/futures-client.ts index 92444d0..aef11f5 100644 --- a/src/futures-client.ts +++ b/src/futures-client.ts @@ -20,6 +20,10 @@ import { FuturesMarginMode, FuturesPosition, NewFuturesPlanTrailingStopOrder, + VIPFeeRate, + GetHistoricTradesParams, + FuturesMarketTrade, + FuturesPlanType, } from './types'; import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; @@ -40,7 +44,7 @@ export class FuturesClient extends BaseRestClient { /** Get Symbols : Get basic configuration information of all trading pairs (including rules) */ getSymbols( - productType: FuturesProductType + productType: FuturesProductType, ): Promise> { return this.get('/api/mix/v1/market/contracts', { productType }); } @@ -60,8 +64,33 @@ export class FuturesClient extends BaseRestClient { return this.get('/api/mix/v1/market/tickers', { productType }); } - /** Get Market Trades */ - getMarketTrades(symbol: string, limit?: string): Promise> { + /** Get VIP fee rates */ + getVIPFeeRates(): Promise> { + return this.get('/api/mix/v1/market/contract-vip-level'); + } + + /** Get most recent trades (up to 500, 100 by default) */ + getRecentTrades( + symbol: string, + limit?: string, + ): Promise> { + return this.get('/api/mix/v1/market/fills', { symbol, limit }); + } + + /** Get historic trades, up to 30 days at a time. Same-parameter responses are cached for 10 minutes. */ + getHistoricTrades( + params: GetHistoricTradesParams, + ): Promise> { + return this.get('/api/mix/v1/market/fills-history', params); + } + + /** + * @deprecated use getRecentTrades() instead. This method will be removed soon. + */ + getMarketTrades( + symbol: string, + limit?: string, + ): Promise> { return this.get('/api/mix/v1/market/fills', { symbol, limit }); } @@ -70,13 +99,15 @@ export class FuturesClient extends BaseRestClient { symbol: string, granularity: KlineInterval, startTime: string, - endTime: string + endTime: string, + limit?: string, ): Promise { return this.get('/api/mix/v1/market/candles', { symbol, granularity, startTime, endTime, + limit, }); } @@ -95,7 +126,7 @@ export class FuturesClient extends BaseRestClient { symbol: string, pageSize?: string, pageNo?: string, - nextPage?: boolean + nextPage?: boolean, ): Promise> { return this.get('/api/mix/v1/market/history-fundRate', { symbol, @@ -134,7 +165,7 @@ export class FuturesClient extends BaseRestClient { /** Get Single Account */ getAccount( symbol: string, - marginCoin: string + marginCoin: string, ): Promise> { return this.getPrivate('/api/mix/v1/account/account', { symbol, @@ -147,6 +178,15 @@ export class FuturesClient extends BaseRestClient { return this.getPrivate('/api/mix/v1/account/accounts', { productType }); } + /** Get Sub Account Contract Assets */ + getSubAccountContractAssets( + productType: FuturesProductType, + ): Promise> { + return this.postPrivate('/api/mix/v1/account/sub-account-contract-assets', { + productType, + }); + } + /** * This interface is only used to calculate the maximum number of positions that can be opened when the user does not hold a position by default. * The result does not represent the actual number of positions opened. @@ -156,7 +196,7 @@ export class FuturesClient extends BaseRestClient { marginCoin: string, openPrice: number, openAmount: number, - leverage?: number + leverage?: number, ): Promise> { return this.postPrivate('/api/mix/v1/account/open-count', { symbol, @@ -172,7 +212,7 @@ export class FuturesClient extends BaseRestClient { symbol: string, marginCoin: string, leverage: string, - holdSide?: string + holdSide?: string, ): Promise> { return this.postPrivate('/api/mix/v1/account/setLeverage', { symbol, @@ -187,7 +227,7 @@ export class FuturesClient extends BaseRestClient { symbol: string, marginCoin: string, amount: string, - holdSide?: string + holdSide?: string, ): Promise> { return this.postPrivate('/api/mix/v1/account/setMargin', { symbol, @@ -201,7 +241,7 @@ export class FuturesClient extends BaseRestClient { setMarginMode( symbol: string, marginCoin: string, - marginMode: FuturesMarginMode + marginMode: FuturesMarginMode, ): Promise> { return this.postPrivate('/api/mix/v1/account/setMarginMode', { symbol, @@ -210,10 +250,21 @@ export class FuturesClient extends BaseRestClient { }); } + /** Change Hold Mode */ + setHoldMode( + productType: FuturesProductType, + holdMode: 'single_hold' | 'double_hold', + ): Promise> { + return this.postPrivate('/api/mix/v1/account/setPositionMode', { + productType, + holdMode, + }); + } + /** Get Symbol Position */ getPosition( symbol: string, - marginCoin?: string + marginCoin?: string, ): Promise> { return this.getPrivate('/api/mix/v1/position/singlePosition', { symbol, @@ -224,7 +275,7 @@ export class FuturesClient extends BaseRestClient { /** Get All Position */ getPositions( productType: FuturesProductType, - marginCoin?: string + marginCoin?: string, ): Promise> { return this.getPrivate('/api/mix/v1/position/allPosition', { productType, @@ -239,7 +290,7 @@ export class FuturesClient extends BaseRestClient { /** Get Business Account Bill */ getBusinessBill( - params: FuturesBusinessBillRequest + params: FuturesBusinessBillRequest, ): Promise> { return this.getPrivate('/api/mix/v1/account/accountBusinessBill', params); } @@ -259,7 +310,7 @@ export class FuturesClient extends BaseRestClient { batchSubmitOrder( symbol: string, marginCoin: string, - orders: NewBatchFuturesOrder[] + orders: NewBatchFuturesOrder[], ): Promise> { return this.postPrivate('/api/mix/v1/order/batch-orders', { symbol, @@ -272,12 +323,14 @@ export class FuturesClient extends BaseRestClient { cancelOrder( symbol: string, marginCoin: string, - orderId: string + orderId?: string, + clientOid?: string, ): Promise> { return this.postPrivate('/api/mix/v1/order/cancel-order', { symbol, marginCoin, orderId, + clientOid, }); } @@ -285,7 +338,7 @@ export class FuturesClient extends BaseRestClient { batchCancelOrder( symbol: string, marginCoin: string, - orderIds: string[] + orderIds: string[], ): Promise> { return this.postPrivate('/api/mix/v1/order/cancel-batch-orders', { symbol, @@ -294,10 +347,23 @@ export class FuturesClient extends BaseRestClient { }); } + /** + * Cancel all futures orders for a symbol + */ + cancelSymbolOrders( + symbol: string, + marginCoin: string, + ): Promise> { + return this.postPrivate('/api/mix/v1/order/cancel-symbol-orders', { + symbol, + marginCoin, + }); + } + /** Cancel All Order */ cancelAllOrders( productType: FuturesProductType, - marginCoin: string + marginCoin: string, ): Promise> { return this.postPrivate('/api/mix/v1/order/cancel-all-orders', { productType, @@ -313,7 +379,7 @@ export class FuturesClient extends BaseRestClient { /** Get All Open Order */ getOpenOrders( productType: FuturesProductType, - marginCoin: string + marginCoin: string, ): Promise> { return this.getPrivate('/api/mix/v1/order/marginCoinCurrent', { productType, @@ -328,7 +394,8 @@ export class FuturesClient extends BaseRestClient { endTime: string, pageSize: string, lastEndId?: string, - isPre?: boolean + isPre?: boolean, + clientOid?: string, ): Promise> { return this.getPrivate('/api/mix/v1/order/history', { symbol, @@ -337,6 +404,7 @@ export class FuturesClient extends BaseRestClient { pageSize, lastEndId, isPre, + clientOid, }); } @@ -347,7 +415,8 @@ export class FuturesClient extends BaseRestClient { endTime: string, pageSize: string, lastEndId?: string, - isPre?: boolean + isPre?: boolean, + clientOid?: string, ): Promise> { return this.getPrivate('/api/mix/v1/order/historyProductType', { productType, @@ -356,6 +425,7 @@ export class FuturesClient extends BaseRestClient { pageSize, lastEndId, isPre, + clientOid, }); } @@ -363,7 +433,7 @@ export class FuturesClient extends BaseRestClient { getOrder( symbol: string, orderId?: string, - clientOid?: string + clientOid?: string, ): Promise> { return this.getPrivate('/api/mix/v1/order/detail', { symbol, @@ -376,7 +446,7 @@ export class FuturesClient extends BaseRestClient { getOrderFills( symbol: string, orderId?: string, - pagination?: FuturesPagination + pagination?: FuturesPagination, ): Promise> { return this.getPrivate('/api/mix/v1/order/fills', { symbol, @@ -388,7 +458,7 @@ export class FuturesClient extends BaseRestClient { /** Get ProductType Order fill detail */ getProductTypeOrderFills( productType: FuturesProductType, - pagination?: FuturesPagination + pagination?: FuturesPagination, ): Promise> { return this.getPrivate('/api/mix/v1/order/allFills', { productType: productType.toUpperCase(), @@ -408,7 +478,7 @@ export class FuturesClient extends BaseRestClient { /** Modify Plan Order TPSL */ modifyPlanOrderTPSL( - params: ModifyFuturesPlanOrderTPSL + params: ModifyFuturesPlanOrderTPSL, ): Promise> { return this.postPrivate('/api/mix/v1/plan/modifyPlanPreset', params); } @@ -420,37 +490,48 @@ export class FuturesClient extends BaseRestClient { /** Place Trailing Stop order */ submitTrailingStopOrder( - params: NewFuturesPlanTrailingStopOrder + params: NewFuturesPlanTrailingStopOrder, ): Promise> { return this.postPrivate('/api/mix/v1/plan/placeTrailStop', params); } /** Place Position TPSL */ submitPositionTPSL( - params: NewFuturesPlanPositionTPSL + params: NewFuturesPlanPositionTPSL, ): Promise> { return this.postPrivate('/api/mix/v1/plan/placePositionsTPSL', params); } /** Modify Stop Order */ modifyStopOrder( - params: ModifyFuturesPlanStopOrder + params: ModifyFuturesPlanStopOrder, ): Promise> { return this.postPrivate('/api/mix/v1/plan/modifyTPSLPlan', params); } /** Cancel Plan Order TPSL */ cancelPlanOrderTPSL( - params: CancelFuturesPlanTPSL + params: CancelFuturesPlanTPSL, ): Promise> { return this.postPrivate('/api/mix/v1/plan/cancelPlan', params); } + /** Cancel All Trigger Order (TPSL) */ + cancelAllPlanOrders( + productType: FuturesProductType, + planType: FuturesPlanType, + ): Promise> { + return this.postPrivate('/api/mix/v1/plan/cancelAllPlan', { + productType, + planType, + }); + } + /** Get Plan Order (TPSL) List */ getPlanOrderTPSLs( symbol: string, isPlan?: string, - productType?: FuturesProductType + productType?: FuturesProductType, ): Promise> { return this.getPrivate('/api/mix/v1/plan/currentPlan', { symbol, @@ -461,14 +542,14 @@ export class FuturesClient extends BaseRestClient { /** Get History Plan Orders (TPSL) */ getHistoricPlanOrdersTPSL( - params: HistoricPlanOrderTPSLRequest + params: HistoricPlanOrderTPSLRequest, ): Promise> { return this.getPrivate('/api/mix/v1/plan/historyPlan', params); } /** * - * Trade Endpoints + * Copy Trade Endpoints * */ @@ -477,7 +558,7 @@ export class FuturesClient extends BaseRestClient { symbol: string, productType: FuturesProductType, pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/currentTrack', { symbol, @@ -492,7 +573,7 @@ export class FuturesClient extends BaseRestClient { symbol: string, productType: FuturesProductType, pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/followerOrder', { symbol, @@ -505,7 +586,7 @@ export class FuturesClient extends BaseRestClient { /** Trader Close Position */ closeCopyTraderPosition( symbol: string, - trackingNo: string + trackingNo: string, ): Promise> { return this.postPrivate('/api/mix/v1/trace/closeTrackOrder', { symbol, @@ -520,7 +601,7 @@ export class FuturesClient extends BaseRestClient { changes?: { stopProfitPrice?: number; stopLossPrice?: number; - } + }, ): Promise> { return this.postPrivate('/api/mix/v1/trace/modifyTPSL', { symbol, @@ -534,7 +615,7 @@ export class FuturesClient extends BaseRestClient { startTime: string, endTime: string, pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/historyTrack', { startTime, @@ -559,7 +640,7 @@ export class FuturesClient extends BaseRestClient { marginCoin: string, dateMs: string, pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/profitDateGroupList', { marginCoin, @@ -574,7 +655,7 @@ export class FuturesClient extends BaseRestClient { marginCoin: string, dateMs: string, pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/profitDateList', { marginCoin, @@ -587,7 +668,7 @@ export class FuturesClient extends BaseRestClient { /** Get Trader Profits Details */ getCopyTraderProfitDetails( pageSize: number, - pageNo: number + pageNo: number, ): Promise> { return this.getPrivate('/api/mix/v1/trace/waitProfitDateList', { pageSize, @@ -603,7 +684,7 @@ export class FuturesClient extends BaseRestClient { /** Trader Change CopyTrade symbol */ setCopyTraderSymbols( symbol: string, - operation: 'add' | 'delete' + operation: 'add' | 'delete', ): Promise> { return this.postPrivate('/api/mix/v1/trace/setUpCopySymbols', { symbol, diff --git a/src/spot-client.ts b/src/spot-client.ts index b239ec3..f57ed72 100644 --- a/src/spot-client.ts +++ b/src/spot-client.ts @@ -7,6 +7,20 @@ import { KlineInterval, CoinBalance, SymbolRules, + NewSpotSubTransfer, + NewSpotWithdraw, + CancelSpotOrderV2, + BatchCancelSpotOrderV2, + SpotOrderResult, + NewSpotPlanOrder, + ModifySpotPlanOrder, + CancelSpotPlanOrderParams, + GetSpotPlanOrdersParams, + SpotPlanOrder, + GetHistoricPlanOrdersParams, + SpotMarketTrade, + GetHistoricTradesParams, + VIPFeeRate, } from './types'; import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; @@ -66,8 +80,28 @@ export class SpotClient extends BaseRestClient { return this.get('/api/spot/v1/market/tickers'); } - /** Get Market Trades */ - getMarketTrades(symbol: string, limit?: string): Promise> { + /** Get most recent trades (up to 500, 100 by default) */ + getRecentTrades( + symbol: string, + limit?: string, + ): Promise> { + return this.get('/api/spot/v1/market/fills', { symbol, limit }); + } + + /** Get historic trades, up to 30 days at a time. Same-parameter responses are cached for 10 minutes. */ + getHistoricTrades( + params: GetHistoricTradesParams, + ): Promise> { + return this.get('/api/spot/v1/market/fills-history', params); + } + + /** + * @deprecated use getRecentTrades() instead. This method will be removed soon. + */ + getMarketTrades( + symbol: string, + limit?: string, + ): Promise> { return this.get('/api/spot/v1/market/fills', { symbol, limit }); } @@ -75,7 +109,7 @@ export class SpotClient extends BaseRestClient { getCandles( symbol: string, period: KlineInterval, - pagination?: Pagination + pagination?: Pagination, ): Promise> { return this.get('/api/spot/v1/market/candles', { symbol, @@ -88,11 +122,16 @@ export class SpotClient extends BaseRestClient { getDepth( symbol: string, type: 'step0' | 'step1' | 'step2' | 'step3' | 'step4' | 'step5', - limit?: string + limit?: string, ): Promise> { return this.get('/api/spot/v1/market/depth', { symbol, type, limit }); } + /** Get VIP fee rates */ + getVIPFeeRates(): Promise> { + return this.get('/api/spot/v1/market/spot-vip-level'); + } + /** * * Wallet Endpoints @@ -104,6 +143,18 @@ export class SpotClient extends BaseRestClient { return this.postPrivate('/api/spot/v1/wallet/transfer', params); } + /** Initiate wallet transfer (v2 endpoint) */ + transferV2(params: NewWalletTransfer): Promise> { + return this.postPrivate('/api/spot/v1/wallet/transfer-v2', params); + } + + /** + * Transfer main-sub, sub-sub or sub-main + */ + subTransfer(params: NewSpotSubTransfer): Promise> { + return this.postPrivate('/api/spot/v1/wallet/subTransfer', params); + } + /** Get Coin Address */ getDepositAddress(coin: string, chain?: string): Promise> { return this.getPrivate('/api/spot/v1/wallet/deposit-address', { @@ -112,25 +163,22 @@ export class SpotClient extends BaseRestClient { }); } - /** Withdraw Coins On Chain*/ - withdraw(params: { - coin: string; - address: string; - chain: string; - tag?: string; - amount: string; - remark?: string; - clientOid?: string; - }): Promise> { + /** Withdraw Coins On Chain */ + withdraw(params: NewSpotWithdraw): Promise> { return this.postPrivate('/api/spot/v1/wallet/withdrawal', params); } + /** Withdraw Coins On Chain (v2 endpoint) */ + withdrawV2(params: NewSpotWithdraw): Promise> { + return this.postPrivate('/api/spot/v1/wallet/withdrawal-v2', params); + } + /** Inner Withdraw : Internal withdrawal means that both users are on the Bitget platform */ innerWithdraw( coin: string, toUid: string, amount: string, - clientOid?: string + clientOid?: string, ): Promise> { return this.postPrivate('/api/spot/v1/wallet/withdrawal-inner', { coin, @@ -140,13 +188,29 @@ export class SpotClient extends BaseRestClient { }); } + /** Inner Withdraw (v2 endpoint) : Internal withdrawal means that both users are on the Bitget platform */ + innerWithdrawV2( + coin: string, + toUid: string, + amount: string, + clientOid?: string, + ): Promise> { + return this.postPrivate('/api/spot/v1/wallet/withdrawal-inner-v2', { + coin, + toUid, + amount, + clientOid, + }); + } + /** Get Withdraw List */ getWithdrawals( coin: string, startTime: string, endTime: string, pageSize?: string, - pageNo?: string + pageNo?: string, + clientOid?: string, ): Promise> { return this.getPrivate('/api/spot/v1/wallet/withdrawal-list', { coin, @@ -154,6 +218,7 @@ export class SpotClient extends BaseRestClient { endTime, pageSize, pageNo, + clientOid, }); } @@ -163,7 +228,7 @@ export class SpotClient extends BaseRestClient { startTime: string, endTime: string, pageSize?: string, - pageNo?: string + pageNo?: string, ): Promise> { return this.getPrivate('/api/spot/v1/wallet/deposit-list', { coin, @@ -190,6 +255,11 @@ export class SpotClient extends BaseRestClient { return this.getPrivate('/api/spot/v1/account/assets', { coin }); } + /** Get sub Account Spot Asset */ + getSubAccountSpotAssets(): Promise> { + return this.postPrivate('/api/spot/v1/account/sub-account-spot-assets'); + } + /** Get Bills : get transaction detail flow */ getTransactionHistory(params?: { coinId?: number; @@ -209,6 +279,7 @@ export class SpotClient extends BaseRestClient { after?: string; before?: string; limit?: number; + clientOid?: string; }): Promise> { return this.getPrivate('/api/spot/v1/account/transferRecords', params); } @@ -220,14 +291,14 @@ export class SpotClient extends BaseRestClient { */ /** Place order */ - submitOrder(params: NewSpotOrder): Promise> { + submitOrder(params: NewSpotOrder): Promise> { return this.postPrivate('/api/spot/v1/trade/orders', params); } /** Place orders in batches, up to 50 at a time */ batchSubmitOrder( symbol: string, - orderList: NewBatchSpotOrder[] + orderList: NewBatchSpotOrder[], ): Promise> { return this.postPrivate('/api/spot/v1/trade/batch-orders', { symbol, @@ -243,10 +314,24 @@ export class SpotClient extends BaseRestClient { }); } + /** Cancel order (v2 endpoint - supports orderId or clientOid) */ + cancelOrderV2(params?: CancelSpotOrderV2): Promise> { + return this.postPrivate('/api/spot/v1/trade/cancel-order-v2', params); + } + + /** + * Cancel all spot orders for a symbol + */ + cancelSymbolOrders(symbol: string): Promise> { + return this.postPrivate('/api/spot/v1/trade/cancel-symbol-order', { + symbol, + }); + } + /** Cancel order in batch (per symbol) */ batchCancelOrder( symbol: string, - orderIds: string[] + orderIds: string[], ): Promise> { return this.postPrivate('/api/spot/v1/trade/cancel-batch-orders', { symbol, @@ -254,11 +339,21 @@ export class SpotClient extends BaseRestClient { }); } + /** Cancel order in batch (per symbol). V2 endpoint, supports orderIds or clientOids. */ + batchCancelOrderV2( + params: BatchCancelSpotOrderV2, + ): Promise> { + return this.postPrivate( + '/api/spot/v1/trade/cancel-batch-orders-v2', + params, + ); + } + /** Get order details */ getOrder( symbol: string, orderId: string, - clientOrderId?: string + clientOrderId?: string, ): Promise> { return this.postPrivate('/api/spot/v1/trade/orderInfo', { symbol, @@ -275,7 +370,7 @@ export class SpotClient extends BaseRestClient { /** Get order history for a symbol */ getOrderHistory( symbol: string, - pagination?: Pagination + pagination?: Pagination, ): Promise> { return this.postPrivate('/api/spot/v1/trade/history', { symbol, @@ -287,7 +382,7 @@ export class SpotClient extends BaseRestClient { getOrderFills( symbol: string, orderId: string, - pagination?: Pagination + pagination?: Pagination, ): Promise> { return this.postPrivate('/api/spot/v1/trade/fills', { symbol, @@ -295,4 +390,47 @@ export class SpotClient extends BaseRestClient { ...pagination, }); } + + /** Place plan order */ + submitPlanOrder( + params: NewSpotPlanOrder, + ): Promise> { + return this.postPrivate('/api/spot/v1/plan/placePlan', params); + } + + /** Modify plan order */ + modifyPlanOrder( + params: ModifySpotPlanOrder, + ): Promise> { + return this.postPrivate('/api/spot/v1/plan/modifyPlan', params); + } + + /** Cancel plan order */ + cancelPlanOrder( + params: CancelSpotPlanOrderParams, + ): Promise> { + return this.postPrivate('/api/spot/v1/plan/cancelPlan', params); + } + + /** Get current plan orders */ + getCurrentPlanOrders(params: GetSpotPlanOrdersParams): Promise< + APIResponse<{ + nextFlag: boolean; + endId: number; + orderList: SpotPlanOrder[]; + }> + > { + return this.postPrivate('/api/spot/v1/plan/currentPlan', params); + } + + /** Get history plan orders */ + getHistoricPlanOrders(params: GetHistoricPlanOrdersParams): Promise< + APIResponse<{ + nextFlag: boolean; + endId: number; + orderList: SpotPlanOrder[]; + }> + > { + return this.postPrivate('/api/spot/v1/plan/historyPlan', params); + } } diff --git a/src/types/request/futures.ts b/src/types/request/futures.ts index 67dfc32..5d934a6 100644 --- a/src/types/request/futures.ts +++ b/src/types/request/futures.ts @@ -51,6 +51,8 @@ export interface NewFuturesOrder { orderType: FuturesOrderType; timeInForceValue?: OrderTimeInForce; clientOid?: string; + reduceOnly?: boolean; + reverse?: boolean; presetTakeProfitPrice?: string; presetStopLossPrice?: string; } @@ -82,6 +84,7 @@ export interface NewFuturesPlanOrder { clientOid?: string; presetTakeProfitPrice?: string; presetStopLossPrice?: string; + reduceOnly?: string; } export interface ModifyFuturesPlanOrder { @@ -90,12 +93,13 @@ export interface ModifyFuturesPlanOrder { symbol: string; executePrice?: string; triggerPrice: string; - triggerType: string; + triggerType: 'fill_price' | 'market_price'; orderType: FuturesOrderType; } export interface ModifyFuturesPlanOrderTPSL { - orderId: string; + orderId?: string; + clientOid?: string; marginCoin: string; symbol: string; presetTakeProfitPrice?: string; @@ -113,6 +117,7 @@ export interface NewFuturesPlanStopOrder { holdSide: FuturesHoldSide; size?: string; rangeRate?: string; + clientOid?: string; } export interface NewFuturesPlanTrailingStopOrder { @@ -123,6 +128,7 @@ export interface NewFuturesPlanTrailingStopOrder { size?: string; side: FuturesOrderSide; rangeRate?: string; + clientOid?: string; } export interface NewFuturesPlanPositionTPSL { @@ -130,18 +136,23 @@ export interface NewFuturesPlanPositionTPSL { marginCoin: string; planType: FuturesPlanType; triggerPrice: string; + triggerType?: 'fill_price' | 'market_price'; holdSide: FuturesHoldSide; + clientOid?: string; } export interface ModifyFuturesPlanStopOrder { - orderId: string; + orderId?: string; + clientOid?: string; marginCoin: string; symbol: string; triggerPrice?: string; + planType: FuturesPlanType; } export interface CancelFuturesPlanTPSL { - orderId: string; + orderId?: string; + clientOid?: string; symbol: string; marginCoin: string; planType: FuturesPlanType; diff --git a/src/types/request/shared.ts b/src/types/request/shared.ts index 0e9bd1e..3a63c26 100644 --- a/src/types/request/shared.ts +++ b/src/types/request/shared.ts @@ -9,3 +9,11 @@ export interface Pagination { } export type OrderTimeInForce = 'normal' | 'post_only' | 'fok' | 'ioc'; + +export interface GetHistoricTradesParams { + symbol: string; + limit?: string; + tradeId?: string; + startTime?: string; + endTime?: string; +} diff --git a/src/types/request/spot.ts b/src/types/request/spot.ts index edc5e46..071f2ee 100644 --- a/src/types/request/spot.ts +++ b/src/types/request/spot.ts @@ -10,6 +10,26 @@ export interface NewWalletTransfer { clientOid?: string; } +export interface NewSpotSubTransfer { + fromType: WalletType; + toType: WalletType; + amount: string; + coin: string; + clientOid: string; + fromUserId: string; + toUserId: string; +} + +export interface NewSpotWithdraw { + coin: string; + address: string; + chain: string; + tag?: string; + amount: string; + remark?: string; + clientOid?: string; +} + export interface NewSpotOrder { symbol: string; side: 'buy' | 'sell'; @@ -21,3 +41,67 @@ export interface NewSpotOrder { } export type NewBatchSpotOrder = Omit; + +export interface CancelSpotOrderV2 { + symbol: string; + orderId?: string; + clientOid?: string; +} + +export interface BatchCancelSpotOrderV2 { + symbol: string; + orderIds?: string[]; + clientOids?: string[]; +} + +export interface NewSpotPlanOrder { + symbol: string; + side: 'buy' | 'sell'; + triggerPrice: number; + executePrice?: number; + size: number; + triggerType: 'fill_price' | 'market_price'; + orderType: 'limit' | 'market'; + clientOid?: string; + timeInForceValue?: string; +} + +export interface NewSpotPlanOrder { + symbol: string; + side: 'buy' | 'sell'; + triggerPrice: number; + executePrice?: number; + size: number; + triggerType: 'fill_price' | 'market_price'; + orderType: 'limit' | 'market'; + clientOid?: string; + timeInForceValue?: string; +} + +export interface ModifySpotPlanOrder { + orderId?: string; + clientOid?: string; + triggerPrice: number; + executePrice?: number; + size?: string; + orderType: 'limit' | 'market'; +} + +export interface CancelSpotPlanOrderParams { + orderId?: string; + clientOid?: string; +} + +export interface GetSpotPlanOrdersParams { + symbol: string; + pageSize: string; + lastEndId?: string; +} + +export interface GetHistoricPlanOrdersParams { + symbol: string; + pageSize: string; + lastEndId?: string; + startTime: string; + endTime: string; +} diff --git a/src/types/response/futures.ts b/src/types/response/futures.ts index ae4dcb7..f195bd4 100644 --- a/src/types/response/futures.ts +++ b/src/types/response/futures.ts @@ -4,6 +4,15 @@ import { FuturesMarginMode, } from '../request'; +export interface FuturesMarketTrade { + tradeId: string; + price: string; + size: string; + side: 'buy' | 'sell'; + timestamp: string; + symbol: string; +} + export interface FuturesAccount { marginCoin: string; locked: number; diff --git a/src/types/response/shared.ts b/src/types/response/shared.ts index e97e286..d99b11c 100644 --- a/src/types/response/shared.ts +++ b/src/types/response/shared.ts @@ -4,3 +4,13 @@ export interface APIResponse { msg: 'success' | string; requestTime: number; } + +export interface VIPFeeRate { + level: number; + dealAmount: string; + assetAmount: string; + takerFeeRate?: string; + makerFeeRate?: number; + withdrawAmount: string; + withdrawAmountUSDT: string; +} diff --git a/src/types/response/spot.ts b/src/types/response/spot.ts index 4c6ea61..4cde265 100644 --- a/src/types/response/spot.ts +++ b/src/types/response/spot.ts @@ -20,3 +20,31 @@ export interface SymbolRules { quantityScale: string; status: string; } +export interface SpotOrderResult { + orderId: string; + clientOrderId: string; +} + +export interface SpotPlanOrder { + orderId: string; + clientOid: string; + symbol: string; + size: string; + executePrice: string; + triggerPrice: string; + status: string; + orderType: string; + side: string; + triggerType: string; + enterPointSource: string; + cTime: number; +} + +export interface SpotMarketTrade { + symbol: string; + tradeId: string; + side: 'buy' | 'sell'; + fillPrice: string; + fillQuantity: string; + fillTime: string; +} diff --git a/src/util/BaseRestClient.ts b/src/util/BaseRestClient.ts index 923140c..1019957 100644 --- a/src/util/BaseRestClient.ts +++ b/src/util/BaseRestClient.ts @@ -44,7 +44,7 @@ export default abstract class BaseRestClient { */ constructor( restOptions: RestClientOptions = {}, - networkOptions: AxiosRequestConfig = {} + networkOptions: AxiosRequestConfig = {}, ) { this.options = { recvWindow: 5000, @@ -77,7 +77,7 @@ export default abstract class BaseRestClient { credentials.some((v) => typeof v === 'string') ) { throw new Error( - 'API Key, Secret & Passphrase are ALL required to use the authenticated REST client' + 'API Key, Secret & Passphrase are ALL required to use the authenticated REST client', ); } } @@ -109,11 +109,11 @@ export default abstract class BaseRestClient { method: Method, endpoint: string, params?: any, - isPublicApi?: boolean + isPublicApi?: boolean, ): Promise { // Sanity check to make sure it's only ever prefixed by one forward slash const requestUrl = [this.baseUrl, endpoint].join( - endpoint.startsWith('/') ? '' : '/' + endpoint.startsWith('/') ? '' : '/', ); // Build a request and handle signature process @@ -122,7 +122,7 @@ export default abstract class BaseRestClient { endpoint, requestUrl, params, - isPublicApi + isPublicApi, ); // console.log('full request: ', options); @@ -188,7 +188,7 @@ export default abstract class BaseRestClient { data: T, endpoint: string, method: Method, - signMethod: SignMethod + signMethod: SignMethod, ): Promise> { const timestamp = Date.now(); @@ -228,7 +228,7 @@ export default abstract class BaseRestClient { console.error( new Date(), - neverGuard(signMethod, `Unhandled sign method: "${signMessage}"`) + neverGuard(signMethod, `Unhandled sign method: "${signMessage}"`), ); return res; @@ -239,21 +239,21 @@ export default abstract class BaseRestClient { endpoint: string, signMethod: SignMethod, params?: TParams, - isPublicApi?: true + isPublicApi?: true, ): Promise>; private async prepareSignParams( method: Method, endpoint: string, signMethod: SignMethod, params?: TParams, - isPublicApi?: false | undefined + isPublicApi?: false | undefined, ): Promise>; private async prepareSignParams( method: Method, endpoint: string, signMethod: SignMethod, params?: TParams, - isPublicApi?: boolean + isPublicApi?: boolean, ) { if (isPublicApi) { return { @@ -275,7 +275,7 @@ export default abstract class BaseRestClient { endpoint: string, url: string, params?: any, - isPublicApi?: boolean + isPublicApi?: boolean, ): Promise { const options: AxiosRequestConfig = { ...this.globalRequestOptions, @@ -301,27 +301,33 @@ export default abstract class BaseRestClient { endpoint, 'bitget', params, - isPublicApi + isPublicApi, ); - if (!options.headers) { - options.headers = {}; - } - options.headers['ACCESS-KEY'] = this.apiKey; - options.headers['ACCESS-PASSPHRASE'] = this.apiPass; - options.headers['ACCESS-TIMESTAMP'] = signResult.timestamp; - options.headers['ACCESS-SIGN'] = signResult.sign; - options.headers['Content-Type'] = 'application/json'; + const authHeaders = { + 'ACCESS-KEY': this.apiKey, + 'ACCESS-PASSPHRASE': this.apiPass, + 'ACCESS-TIMESTAMP': signResult.timestamp, + 'ACCESS-SIGN': signResult.sign, + }; if (method === 'GET') { return { ...options, + headers: { + ...authHeaders, + ...options.headers, + }, url: options.url + signResult.queryParamsWithSign, }; } return { ...options, + headers: { + ...authHeaders, + ...options.headers, + }, data: params, }; } diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index c02e1da..1151dea 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -82,7 +82,7 @@ export default class WsStore { if (this.hasExistingActiveConnection(key)) { this.logger.warning( 'WsStore setConnection() overwriting existing open connection: ', - this.getWs(key) + this.getWs(key), ); } this.wsState[key] = { @@ -98,7 +98,7 @@ export default class WsStore { const ws = this.getWs(key); this.logger.warning( 'WsStore deleting state for connection still open: ', - ws + ws, ); ws?.close(); } @@ -119,7 +119,7 @@ export default class WsStore { if (this.isWsOpen(key)) { this.logger.warning( 'WsStore setConnection() overwriting existing open connection: ', - this.getWs(key) + this.getWs(key), ); } diff --git a/src/util/browser-support.ts b/src/util/browser-support.ts index 8098e72..3633549 100644 --- a/src/util/browser-support.ts +++ b/src/util/browser-support.ts @@ -11,7 +11,7 @@ function _arrayBufferToBase64(buffer: ArrayBuffer) { export async function signMessage( message: string, secret: string, - method: 'hex' | 'base64' + method: 'hex' | 'base64', ): Promise { const encoder = new TextEncoder(); const key = await window.crypto.subtle.importKey( @@ -19,20 +19,20 @@ export async function signMessage( encoder.encode(secret), { name: 'HMAC', hash: { name: 'SHA-256' } }, false, - ['sign'] + ['sign'], ); const signature = await window.crypto.subtle.sign( 'HMAC', key, - encoder.encode(message) + encoder.encode(message), ); switch (method) { case 'hex': { return Array.prototype.map .call(new Uint8Array(signature), (x: any) => - ('00' + x.toString(16)).slice(-2) + ('00' + x.toString(16)).slice(-2), ) .join(''); } diff --git a/src/util/node-support.ts b/src/util/node-support.ts index 3d88b55..4afe4b6 100644 --- a/src/util/node-support.ts +++ b/src/util/node-support.ts @@ -4,7 +4,7 @@ import { createHmac } from 'crypto'; export async function signMessage( message: string, secret: string, - method: 'hex' | 'base64' + method: 'hex' | 'base64', ): Promise { const hmac = createHmac('sha256', secret).update(message); diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index e448bdc..63cc78d 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -30,7 +30,7 @@ export interface RestClientOptions { export function serializeParams( params: T, strict_validation = false, - prefixWith: string = '' + prefixWith: string = '', ): string { if (!params) { return ''; @@ -42,7 +42,7 @@ export function serializeParams( const value = params[key]; if (strict_validation === true && typeof value === 'undefined') { throw new Error( - 'Failed to sign API request due to undefined parameter' + 'Failed to sign API request due to undefined parameter', ); } return `${key}=${value}`; @@ -55,7 +55,7 @@ export function serializeParams( export function getRestBaseUrl( useTestnet: boolean, - restInverseOptions: RestClientOptions + restInverseOptions: RestClientOptions, ): string { const exchangeBaseUrls = { livenet: 'https://api.bitget.com', diff --git a/src/util/type-guards.ts b/src/util/type-guards.ts index 2577367..b74709a 100644 --- a/src/util/type-guards.ts +++ b/src/util/type-guards.ts @@ -36,7 +36,7 @@ function isWsChannelEvent(event: WsBaseEvent): event is WsSnapshotChannelEvent { /** TypeGuard: event is an account update (balance) */ export function isWsAccountSnapshotEvent( - event: unknown + event: unknown, ): event is WsSnapshotAccountEvent { return ( isWsSnapshotEvent(event) && @@ -48,7 +48,7 @@ export function isWsAccountSnapshotEvent( /** TypeGuard: event is a positions update */ export function isWsPositionsSnapshotEvent( - event: unknown + event: unknown, ): event is WsSnapshotPositionsEvent { return ( isWsSnapshotEvent(event) && @@ -60,14 +60,14 @@ export function isWsPositionsSnapshotEvent( /** TypeGuard: event is a UMCBL account update (balance) */ export function isWsFuturesAccountSnapshotEvent( - event: unknown + event: unknown, ): event is WsAccountSnapshotUMCBL { return isWsAccountSnapshotEvent(event) && event.arg.instType === 'umcbl'; } /** TypeGuard: event is a UMCBL positions update */ export function isWsFuturesPositionsSnapshotEvent( - event: unknown + event: unknown, ): event is WSPositionSnapshotUMCBL { return isWsPositionsSnapshotEvent(event) && event.arg.instType === 'umcbl'; } diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index 844c873..6b9645b 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -11,7 +11,7 @@ import { BitgetInstType, WsTopicSubscribeEventArgs } from './WsStore'; */ type NetworkMap< TRequiredKeys extends string, - TOptionalKeys extends string | undefined = undefined + TOptionalKeys extends string | undefined = undefined, > = Record & (TOptionalKeys extends string ? Record @@ -55,14 +55,14 @@ export const PUBLIC_WS_KEYS = [] as WsKey[]; export const PRIVATE_TOPICS = ['account', 'orders', 'positions', 'ordersAlgo']; export function isPrivateChannel( - channel: TChannel + channel: TChannel, ): boolean { return PRIVATE_TOPICS.includes(channel); } export function getWsKeyForTopic( subscribeEvent: WsTopicSubscribeEventArgs, - isPrivate?: boolean + isPrivate?: boolean, ): WsKey { const instType = subscribeEvent.instType.toUpperCase() as BitgetInstType; switch (instType) { @@ -78,7 +78,7 @@ export function getWsKeyForTopic( default: { throw neverGuard( instType, - `getWsKeyForTopic(): Unhandled market ${'instrumentId'}` + `getWsKeyForTopic(): Unhandled market ${'instrumentId'}`, ); } } @@ -110,14 +110,14 @@ export async function getWsAuthSignature( apiKey: string | undefined, apiSecret: string | undefined, apiPass: string | undefined, - recvWindow: number = 0 + recvWindow: number = 0, ): Promise<{ expiresAt: number; signature: string; }> { if (!apiKey || !apiSecret || !apiPass) { throw new Error( - `Cannot auth - missing api key, secret or passcode in config` + `Cannot auth - missing api key, secret or passcode in config`, ); } const signatureExpiresAt = ((Date.now() + recvWindow) / 1000).toFixed(0); @@ -125,7 +125,7 @@ export async function getWsAuthSignature( const signature = await signMessage( signatureExpiresAt + 'GET' + '/user/verify', apiSecret, - 'base64' + 'base64', ); return { diff --git a/src/websocket-client.ts b/src/websocket-client.ts index c578a2b..611dad0 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -61,7 +61,7 @@ interface WebsocketClientEvents { export declare interface WebsocketClient { on( event: U, - listener: WebsocketClientEvents[U] + listener: WebsocketClientEvents[U], ): this; emit( @@ -77,7 +77,7 @@ export class WebsocketClient extends EventEmitter { constructor( options: WSClientConfigurableOptions, - logger?: typeof DefaultLogger + logger?: typeof DefaultLogger, ) { super(); @@ -100,7 +100,7 @@ export class WebsocketClient extends EventEmitter { */ public subscribe( wsTopics: WsTopicSubscribeEventArgs[] | WsTopicSubscribeEventArgs, - isPrivateTopic?: boolean + isPrivateTopic?: boolean, ) { const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; @@ -122,7 +122,7 @@ export class WebsocketClient extends EventEmitter { if (!isAuthenticated) { return this.requestSubscribeTopics( wsKey, - topics.filter((topic) => !isPrivateChannel(topic.channel)) + topics.filter((topic) => !isPrivateChannel(topic.channel)), ); } return this.requestSubscribeTopics(wsKey, topics); @@ -132,11 +132,11 @@ export class WebsocketClient extends EventEmitter { if ( !this.wsStore.isConnectionState( wsKey, - WsConnectionStateEnum.CONNECTING + WsConnectionStateEnum.CONNECTING, ) && !this.wsStore.isConnectionState( wsKey, - WsConnectionStateEnum.RECONNECTING + WsConnectionStateEnum.RECONNECTING, ) ) { return this.connect(wsKey); @@ -151,11 +151,11 @@ export class WebsocketClient extends EventEmitter { */ public unsubscribe( wsTopics: WsTopicSubscribeEventArgs[] | WsTopicSubscribeEventArgs, - isPrivateTopic?: boolean + isPrivateTopic?: boolean, ) { const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; topics.forEach((topic) => - this.wsStore.deleteTopic(getWsKeyForTopic(topic, isPrivateTopic), topic) + this.wsStore.deleteTopic(getWsKeyForTopic(topic, isPrivateTopic), topic), ); // TODO: should this really happen on each wsKey?? seems weird @@ -207,7 +207,7 @@ export class WebsocketClient extends EventEmitter { if (this.wsStore.isWsOpen(wsKey)) { this.logger.error( 'Refused to connect to ws with existing active connection', - { ...LOGGER_CATEGORY, wsKey } + { ...LOGGER_CATEGORY, wsKey }, ); return this.wsStore.getWs(wsKey); } @@ -217,7 +217,7 @@ export class WebsocketClient extends EventEmitter { ) { this.logger.error( 'Refused to connect to ws, connection attempt already active', - { ...LOGGER_CATEGORY, wsKey } + { ...LOGGER_CATEGORY, wsKey }, ); return; } @@ -260,7 +260,7 @@ export class WebsocketClient extends EventEmitter { `${context} due to unexpected response error: "${ error?.msg || error?.message || error }"`, - { ...LOGGER_CATEGORY, wsKey, error } + { ...LOGGER_CATEGORY, wsKey, error }, ); break; } @@ -278,7 +278,7 @@ export class WebsocketClient extends EventEmitter { apiKey, apiSecret, apiPass, - recvWindow + recvWindow, ); this.logger.info(`Sending auth request...`, { @@ -375,7 +375,7 @@ export class WebsocketClient extends EventEmitter { */ private requestSubscribeTopics( wsKey: WsKey, - topics: WsTopicSubscribeEventArgs[] + topics: WsTopicSubscribeEventArgs[], ) { if (!topics.length) { return; @@ -384,7 +384,7 @@ export class WebsocketClient extends EventEmitter { const maxTopicsPerEvent = getMaxTopicsPerSubscribeEvent(wsKey); if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) { this.logger.silly( - `Subscribing to topics in batches of ${maxTopicsPerEvent}` + `Subscribing to topics in batches of ${maxTopicsPerEvent}`, ); for (var i = 0; i < topics.length; i += maxTopicsPerEvent) { const batch = topics.slice(i, i + maxTopicsPerEvent); @@ -392,7 +392,7 @@ export class WebsocketClient extends EventEmitter { this.requestSubscribeTopics(wsKey, batch); } this.logger.silly( - `Finished batch subscribing to ${topics.length} topics` + `Finished batch subscribing to ${topics.length} topics`, ); return; } @@ -410,7 +410,7 @@ export class WebsocketClient extends EventEmitter { */ private requestUnsubscribeTopics( wsKey: WsKey, - topics: WsTopicSubscribeEventArgs[] + topics: WsTopicSubscribeEventArgs[], ) { if (!topics.length) { return; @@ -419,7 +419,7 @@ export class WebsocketClient extends EventEmitter { const maxTopicsPerEvent = getMaxTopicsPerSubscribeEvent(wsKey); if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) { this.logger.silly( - `Unsubscribing to topics in batches of ${maxTopicsPerEvent}` + `Unsubscribing to topics in batches of ${maxTopicsPerEvent}`, ); for (var i = 0; i < topics.length; i += maxTopicsPerEvent) { const batch = topics.slice(i, i + maxTopicsPerEvent); @@ -427,7 +427,7 @@ export class WebsocketClient extends EventEmitter { this.requestUnsubscribeTopics(wsKey, batch); } this.logger.silly( - `Finished batch unsubscribing to ${topics.length} topics` + `Finished batch unsubscribing to ${topics.length} topics`, ); return; } @@ -449,13 +449,13 @@ export class WebsocketClient extends EventEmitter { }); if (!wsKey) { throw new Error( - 'Cannot send message due to no known websocket for this wsKey' + 'Cannot send message due to no known websocket for this wsKey', ); } const ws = this.getWs(wsKey); if (!ws) { throw new Error( - `${wsKey} socket not connected yet, call "connectAll()" first then try again when the "open" event arrives` + `${wsKey} socket not connected yet, call "connectAll()" first then try again when the "open" event arrives`, ); } ws.send(wsMessage); @@ -512,13 +512,13 @@ export class WebsocketClient extends EventEmitter { // Private topics will be resubscribed to once reconnected const topics = [...this.wsStore.getTopics(wsKey)]; const publicTopics = topics.filter( - (topic) => !isPrivateChannel(topic.channel) + (topic) => !isPrivateChannel(topic.channel), ); this.requestSubscribeTopics(wsKey, publicTopics); this.wsStore.get(wsKey, true)!.activePingTimer = setInterval( () => this.ping(wsKey), - this.options.pingInterval + this.options.pingInterval, ); } @@ -529,7 +529,7 @@ export class WebsocketClient extends EventEmitter { const topics = [...this.wsStore.getTopics(wsKey)]; const privateTopics = topics.filter((topic) => - isPrivateChannel(topic.channel) + isPrivateChannel(topic.channel), ); if (privateTopics.length) { @@ -665,7 +665,7 @@ export class WebsocketClient extends EventEmitter { public subscribeTopic( instType: BitgetInstType, topic: WsTopic, - instId: string = 'default' + instId: string = 'default', ) { return this.subscribe({ instType, @@ -683,7 +683,7 @@ export class WebsocketClient extends EventEmitter { public unsubscribeTopic( instType: BitgetInstType, topic: WsTopic, - instId: string = 'default' + instId: string = 'default', ) { return this.unsubscribe({ instType, diff --git a/test/broker/private.read.test.ts b/test/broker/private.read.test.ts index 67522a9..cb92c57 100644 --- a/test/broker/private.read.test.ts +++ b/test/broker/private.read.test.ts @@ -27,7 +27,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getBrokerInfo()', async () => { try { expect(await api.getBrokerInfo()).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -39,7 +39,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubAccounts()', async () => { try { expect(await api.getSubAccounts()).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -51,7 +51,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubEmail()', async () => { try { expect(await api.getSubEmail(subUid)).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -63,7 +63,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubSpotAssets()', async () => { try { expect(await api.getSubSpotAssets(subUid)).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { // expect(e.body).toBeNull(); @@ -76,7 +76,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubFutureAssets()', async () => { try { expect(await api.getSubFutureAssets(subUid, 'usdt')).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -88,7 +88,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubDepositAddress()', async () => { try { expect(await api.getSubDepositAddress(subUid, coin)).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -100,7 +100,7 @@ describe('Private Broker REST API GET Endpoints', () => { it('getSubAPIKeys()', async () => { try { expect(await api.getSubAPIKeys(subUid)).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ diff --git a/test/broker/private.write.test.ts b/test/broker/private.write.test.ts index a7e15ff..c9dd3ef 100644 --- a/test/broker/private.write.test.ts +++ b/test/broker/private.write.test.ts @@ -27,7 +27,7 @@ describe('Private Broker REST API POST Endpoints', () => { it('createSubAccount()', async () => { try { expect(await api.createSubAccount('test1')).toMatchObject( - sucessEmptyResponseObject() + sucessEmptyResponseObject(), ); } catch (e) { expect(e.body).toMatchObject({ @@ -39,7 +39,7 @@ describe('Private Broker REST API POST Endpoints', () => { it('modifySubAccount()', async () => { try { expect( - await api.modifySubAccount('test1', 'spot_trade,transfer', 'normal') + await api.modifySubAccount('test1', 'spot_trade,transfer', 'normal'), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ @@ -51,7 +51,7 @@ describe('Private Broker REST API POST Endpoints', () => { it('modifySubEmail()', async () => { try { expect( - await api.modifySubEmail('test1', 'ASDFASDF@LKMASDF.COM') + await api.modifySubEmail('test1', 'ASDFASDF@LKMASDF.COM'), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ @@ -69,7 +69,7 @@ describe('Private Broker REST API POST Endpoints', () => { chain: 'TRC20', coin: 'USDT', subUid, - }) + }), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ @@ -81,7 +81,7 @@ describe('Private Broker REST API POST Endpoints', () => { it('setSubDepositAutoTransfer()', async () => { try { expect( - await api.setSubDepositAutoTransfer(subUid, 'USDT', 'spot') + await api.setSubDepositAutoTransfer(subUid, 'USDT', 'spot'), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ @@ -97,8 +97,8 @@ describe('Private Broker REST API POST Endpoints', () => { subUid, 'passphrase12345', 'remark', - '10.0.0.1' - ) + '10.0.0.1', + ), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ @@ -114,7 +114,7 @@ describe('Private Broker REST API POST Endpoints', () => { apikey: '12345', subUid, remark: 'test', - }) + }), ).toMatchObject(sucessEmptyResponseObject()); } catch (e) { expect(e.body).toMatchObject({ diff --git a/test/futures/private.read.test.ts b/test/futures/private.read.test.ts index 4adc392..b117c50 100644 --- a/test/futures/private.read.test.ts +++ b/test/futures/private.read.test.ts @@ -57,7 +57,7 @@ describe('Private Futures REST API GET Endpoints', () => { it('getOpenCount()', async () => { try { expect( - await api.getOpenCount(symbol, marginCoin, 20000, 1) + await api.getOpenCount(symbol, marginCoin, 20000, 1), ).toMatchObject({ ...sucessEmptyResponseObject(), data: { @@ -102,7 +102,7 @@ describe('Private Futures REST API GET Endpoints', () => { endTime: to, marginCoin, symbol, - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: { @@ -125,7 +125,7 @@ describe('Private Futures REST API GET Endpoints', () => { startTime: from, endTime: to, productType: 'umcbl', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: { @@ -180,7 +180,7 @@ describe('Private Futures REST API GET Endpoints', () => { it('getProductTypeOrderHistory()', async () => { try { expect( - await api.getProductTypeOrderHistory('umcbl', from, to, '10') + await api.getProductTypeOrderHistory('umcbl', from, to, '10'), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -223,7 +223,7 @@ describe('Private Futures REST API GET Endpoints', () => { await api.getProductTypeOrderFills('umcbl', { startTime: from, endTime: to, - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -253,7 +253,7 @@ describe('Private Futures REST API GET Endpoints', () => { startTime: from, endTime: to, symbol, - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -267,7 +267,7 @@ describe('Private Futures REST API GET Endpoints', () => { it('getCopyTraderOpenOrder()', async () => { try { expect( - await api.getCopyTraderOpenOrder(symbol, 'umcbl', 1, 0) + await api.getCopyTraderOpenOrder(symbol, 'umcbl', 1, 0), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -282,7 +282,7 @@ describe('Private Futures REST API GET Endpoints', () => { it('getCopyFollowersOpenOrder()', async () => { try { expect( - await api.getCopyFollowersOpenOrder(symbol, 'umcbl', 1, 0) + await api.getCopyFollowersOpenOrder(symbol, 'umcbl', 1, 0), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -300,7 +300,7 @@ describe('Private Futures REST API GET Endpoints', () => { { ...sucessEmptyResponseObject(), data: expect.any(Object), - } + }, ); } catch (e) { expect(e.body).toMatchObject({ @@ -342,8 +342,8 @@ describe('Private Futures REST API GET Endpoints', () => { marginCoin, from, 1, - 1 - ) + 1, + ), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), @@ -358,7 +358,7 @@ describe('Private Futures REST API GET Endpoints', () => { it('getCopyTraderHistoricProfitDetail()', async () => { try { expect( - await api.getCopyTraderHistoricProfitDetail(marginCoin, from, 1, 1) + await api.getCopyTraderHistoricProfitDetail(marginCoin, from, 1, 1), ).toMatchObject({ ...sucessEmptyResponseObject(), data: expect.any(Object), diff --git a/test/futures/private.write.test.ts b/test/futures/private.write.test.ts index d4464f4..5446cca 100644 --- a/test/futures/private.write.test.ts +++ b/test/futures/private.write.test.ts @@ -53,7 +53,7 @@ describe('Private Futures REST API POST Endpoints', () => { it('setMarginMode()', async () => { try { expect( - await api.setMarginMode(symbol, marginCoin, 'crossed') + await api.setMarginMode(symbol, marginCoin, 'crossed'), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -76,7 +76,7 @@ describe('Private Futures REST API POST Endpoints', () => { symbol, size: '1', side: 'open_long', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -100,7 +100,7 @@ describe('Private Futures REST API POST Endpoints', () => { size: '1', side: 'open_long', }, - ]) + ]), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -115,7 +115,7 @@ describe('Private Futures REST API POST Endpoints', () => { it('cancelOrder()', async () => { try { expect( - await api.cancelOrder(symbol, marginCoin, '1234656') + await api.cancelOrder(symbol, marginCoin, '1234656'), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -130,7 +130,7 @@ describe('Private Futures REST API POST Endpoints', () => { it('batchCancelOrder()', async () => { try { expect( - await api.batchCancelOrder(symbol, marginCoin, ['1234656']) + await api.batchCancelOrder(symbol, marginCoin, ['1234656']), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -164,7 +164,7 @@ describe('Private Futures REST API POST Endpoints', () => { symbol, triggerPrice: '1000', triggerType: 'market_price', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -187,7 +187,7 @@ describe('Private Futures REST API POST Endpoints', () => { symbol, triggerPrice: '100', triggerType: 'market_price', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -207,7 +207,7 @@ describe('Private Futures REST API POST Endpoints', () => { marginCoin, symbol, presetTakeProfitPrice: '100', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -229,7 +229,7 @@ describe('Private Futures REST API POST Endpoints', () => { planType: 'profit_plan', holdSide: 'long', triggerPrice: '100', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -251,7 +251,7 @@ describe('Private Futures REST API POST Endpoints', () => { holdSide: 'long', planType: 'profit_plan', triggerPrice: '50', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -270,7 +270,8 @@ describe('Private Futures REST API POST Endpoints', () => { marginCoin, symbol, orderId: '123456', - }) + planType: 'profit_plan', + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -278,8 +279,7 @@ describe('Private Futures REST API POST Endpoints', () => { } catch (e) { // expect(e).toBeNull(); expect(e.body).toMatchObject({ - // code: API_ERROR_CODE.FUTURES_ORDER_TPSL_NOT_FOUND, - code: API_ERROR_CODE.FUTURES_POSITION_DIRECTION_EMPTY, + code: API_ERROR_CODE.FUTURES_ORDER_TPSL_NOT_FOUND, }); } }); @@ -292,7 +292,7 @@ describe('Private Futures REST API POST Endpoints', () => { symbol, orderId: '123456', planType: 'profit_plan', - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, @@ -310,7 +310,7 @@ describe('Private Futures REST API POST Endpoints', () => { { ...sucessEmptyResponseObject(), data: {}, - } + }, ); } catch (e) { expect(e.body).toMatchObject({ @@ -324,7 +324,7 @@ describe('Private Futures REST API POST Endpoints', () => { expect( await api.modifyCopyTraderTPSL(symbol, '123456', { stopLossPrice: 1234, - }) + }), ).toMatchObject({ ...sucessEmptyResponseObject(), data: {}, diff --git a/test/futures/public.test.ts b/test/futures/public.test.ts index 804e2ae..2076fe4 100644 --- a/test/futures/public.test.ts +++ b/test/futures/public.test.ts @@ -70,7 +70,7 @@ describe('Public Spot REST API Endpoints', () => { it('getCandles()', async () => { expect( - await api.getCandles(symbol, '1min', `${from}`, `${to}`) + await api.getCandles(symbol, '1min', `${from}`, `${to}`), ).toMatchObject(expect.any(Array)); }); diff --git a/test/response.util.ts b/test/response.util.ts index 4d704ce..6679da0 100644 --- a/test/response.util.ts +++ b/test/response.util.ts @@ -19,7 +19,7 @@ export function sucessEmptyResponseObject() { export function errorResponseObject( result: null | any = null, ret_code: number, - ret_msg: string + ret_msg: string, ) { return { result, @@ -30,7 +30,7 @@ export function errorResponseObject( export function errorResponseObjectV3( result: null | any = null, - retCode: number + retCode: number, ) { return { result, diff --git a/test/spot/private.read.test.ts b/test/spot/private.read.test.ts index e7ac101..8815caa 100644 --- a/test/spot/private.read.test.ts +++ b/test/spot/private.read.test.ts @@ -159,4 +159,44 @@ describe('Private Spot REST API GET Endpoints', () => { expect(e).toBeNull(); } }); + + it('getCurrentPlanOrders()', async () => { + try { + expect( + await api.getCurrentPlanOrders({ symbol, pageSize: '20' }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: { + nextFlag: false, + orderList: expect.any(Array), + }, + }); + } catch (e) { + console.error('getCurrentPlanOrders: ', e); + expect(e).toBeNull(); + } + }); + + it('getHistoricPlanOrders()', async () => { + try { + expect( + await api.getHistoricPlanOrders({ + symbol, + pageSize: '20', + startTime: '1667889483000', + endTime: '1668134732000', + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: { + endId: null, + nextFlag: false, + orderList: expect.any(Array), + }, + }); + } catch (e) { + console.error('getHistoricPlanOrders: ', e); + expect(e).toBeNull(); + } + }); }); diff --git a/test/spot/private.write.test.ts b/test/spot/private.write.test.ts index 280a764..aa0dc91 100644 --- a/test/spot/private.write.test.ts +++ b/test/spot/private.write.test.ts @@ -20,129 +20,263 @@ describe('Private Spot REST API POST Endpoints', () => { const symbol = 'BTCUSDT_SPBL'; const coin = 'USDT'; - const timestampOneHourAgo = new Date().getTime() - 1000 * 60 * 60; - const from = timestampOneHourAgo.toFixed(0); - const to = String(Number(from) + 1000 * 60 * 30); // 30 minutes - it('transfer()', async () => { - try { - expect( - await api.transfer({ - amount: '100', - coin, - fromType: 'spot', - toType: 'mix_usdt', - }) - ).toStrictEqual(''); - } catch (e) { - // console.error('transfer: ', e); - expect(e.body).toMatchObject({ - // not sure what this error means, probably no balance. Seems to change? - code: expect.stringMatching(/42013|43117/gim), - }); - } + describe('transfers', () => { + it('transfer()', async () => { + try { + expect( + await api.transfer({ + amount: '100', + coin, + fromType: 'spot', + toType: 'mix_usdt', + }), + ).toStrictEqual(''); + } catch (e) { + // console.error('transfer: ', e); + expect(e.body).toMatchObject({ + // not sure what this error means, probably no balance. Seems to change? + code: expect.stringMatching(/42013|43117/gim), + }); + } + }); + + it('transferV2()', async () => { + try { + expect( + await api.transferV2({ + amount: '100', + coin, + fromType: 'spot', + toType: 'mix_usdt', + }), + ).toStrictEqual(''); + } catch (e) { + // console.error('transferV2: ', e); + expect(e.body).toMatchObject({ + // not sure what this error means, probably no balance. Seems to change? + code: expect.stringMatching(/42013|43117/gim), + }); + } + }); + + it('subTransfer()', async () => { + try { + expect( + await api.subTransfer({ + fromUserId: '123', + toUserId: '456', + amount: '100', + clientOid: '123456', + coin, + fromType: 'spot', + toType: 'mix_usdt', + }), + ).toStrictEqual(''); + } catch (e) { + // console.error('transferV2: ', e); + expect(e.body).toMatchObject({ + // not sure what this error means, probably no balance. Seems to change? + code: expect.stringMatching(/42013|43117|40018/gim), + }); + } + }); + + it('withdraw()', async () => { + try { + expect( + await api.withdraw({ + amount: '100', + coin, + chain: 'TRC20', + address: `123456`, + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.INCORRECT_PERMISSIONS, + }); + } + }); + + it('withdrawV2()', async () => { + try { + expect( + await api.withdrawV2({ + amount: '100', + coin, + chain: 'TRC20', + address: `123456`, + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.INCORRECT_PERMISSIONS, + }); + } + }); + + it('innerWithdraw()', async () => { + try { + expect(await api.innerWithdraw(coin, '12345', '1')).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.INCORRECT_PERMISSIONS, + }); + } + }); + + it('innerWithdrawV2()', async () => { + try { + expect(await api.innerWithdrawV2(coin, '12345', '1')).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.INCORRECT_PERMISSIONS, + }); + } + }); }); - - it('withdraw()', async () => { - try { - expect( - await api.withdraw({ - amount: '100', - coin, - chain: 'TRC20', - address: `123456`, - }) - ).toMatchObject({ - ...sucessEmptyResponseObject(), - data: expect.any(Array), - }); - } catch (e) { - expect(e.body).toMatchObject({ - code: API_ERROR_CODE.INCORRECT_PERMISSIONS, - }); - } - }); - - it('innerWithdraw()', async () => { - try { - expect(await api.innerWithdraw(coin, '12345', '1')).toMatchObject({ - ...sucessEmptyResponseObject(), - data: expect.any(Array), - }); - } catch (e) { - expect(e.body).toMatchObject({ - code: API_ERROR_CODE.INCORRECT_PERMISSIONS, - }); - } - }); - - it('submitOrder()', async () => { - try { - expect( - await api.submitOrder({ - symbol, - side: 'buy', - orderType: 'market', - quantity: '1', - force: 'normal', - }) - ).toMatchObject({ - ...sucessEmptyResponseObject(), - data: expect.any(Array), - }); - } catch (e) { - expect(e.body).toMatchObject({ - code: API_ERROR_CODE.QTY_LESS_THAN_MINIMUM, - }); - } - }); - - it('batchSubmitOrder()', async () => { - try { - expect( - await api.batchSubmitOrder(symbol, [ - { + describe('orders', () => { + it('submitOrder()', async () => { + try { + expect( + await api.submitOrder({ + symbol, side: 'buy', orderType: 'market', quantity: '1', force: 'normal', + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.QTY_LESS_THAN_MINIMUM, + }); + } + }); + + it('batchSubmitOrder()', async () => { + try { + expect( + await api.batchSubmitOrder(symbol, [ + { + side: 'buy', + orderType: 'market', + quantity: '1', + force: 'normal', + }, + ]), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: { + resultList: expect.any(Array), + failure: [{ errorCode: API_ERROR_CODE.QTY_LESS_THAN_MINIMUM }], }, - ]) - ).toMatchObject({ - ...sucessEmptyResponseObject(), - data: { - resultList: expect.any(Array), - failure: [{ errorCode: API_ERROR_CODE.QTY_LESS_THAN_MINIMUM }], - }, - }); - } catch (e) { - expect(e).toBeNull(); - } + }); + } catch (e) { + expect(e).toBeNull(); + } + }); + + it('cancelOrder()', async () => { + try { + expect(await api.cancelOrder(symbol, '123456')).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.ORDER_NOT_FOUND, + }); + } + }); + + it('batchCancelOrder()', async () => { + try { + expect(await api.batchCancelOrder(symbol, ['123456'])).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.ORDER_NOT_FOUND, + }); + } + }); }); - it('cancelOrder()', async () => { - try { - expect(await api.cancelOrder(symbol, '123456')).toMatchObject({ - ...sucessEmptyResponseObject(), - data: expect.any(Array), - }); - } catch (e) { - expect(e.body).toMatchObject({ - code: API_ERROR_CODE.ORDER_NOT_FOUND, - }); - } - }); + describe('plan orders', () => { + let planOrderId: string; - it('batchCancelOrder()', async () => { - try { - expect(await api.batchCancelOrder(symbol, ['123456'])).toMatchObject({ - ...sucessEmptyResponseObject(), - data: expect.any(Array), - }); - } catch (e) { - expect(e.body).toMatchObject({ - code: API_ERROR_CODE.ORDER_NOT_FOUND, - }); - } + it('submitPlanOrder()', async () => { + try { + const result = await api.submitPlanOrder({ + symbol, + side: 'buy', + orderType: 'market', + size: 100, + triggerPrice: 100, + triggerType: 'fill_price', + }); + + planOrderId = result.data.orderId; + expect(result).toMatchObject({ + ...sucessEmptyResponseObject(), + }); + } catch (e) { + console.error('submitPlanOrder(): ', e); + expect(e).toBeNull(); + } + }); + + it('modifyPlanOrder()', async () => { + try { + expect( + await api.modifyPlanOrder({ + orderType: 'market', + triggerPrice: 100, + orderId: '123456', + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(Array), + }); + } catch (e) { + expect(e.body).toMatchObject({ + code: API_ERROR_CODE.PLAN_ORDER_NOT_FOUND, + }); + } + }); + + it('cancelPlanOrder()', async () => { + try { + expect( + await api.cancelPlanOrder({ + orderId: planOrderId || '123456', + }), + ).toMatchObject({ + ...sucessEmptyResponseObject(), + data: expect.any(String), + }); + } catch (e) { + console.error('cancelPlanOrder(): ', e); + expect(e).toBeNull(); + } + }); }); }); diff --git a/test/ws.private.test.ts b/test/ws.private.test.ts index 3c735b5..7354069 100644 --- a/test/ws.private.test.ts +++ b/test/ws.private.test.ts @@ -26,7 +26,7 @@ describe('Private Spot Websocket Client', () => { apiSecret: 'bad', apiPass: 'bad', }, - getSilentLogger('expect401') + getSilentLogger('expect401'), ); // const wsOpenPromise = waitForSocketEvent(badClient, 'open'); @@ -62,7 +62,7 @@ describe('Private Spot Websocket Client', () => { beforeAll(() => { wsClient = new WebsocketClient( wsClientOptions, - getSilentLogger('expectSuccess') + getSilentLogger('expectSuccess'), ); wsClient.connectAll(); // logAllEvents(wsClient); diff --git a/test/ws.public.test.ts b/test/ws.public.test.ts index 815f48d..0b6539b 100644 --- a/test/ws.public.test.ts +++ b/test/ws.public.test.ts @@ -13,7 +13,7 @@ describe('Public Spot Websocket Client', () => { beforeAll(() => { wsClient = new WebsocketClient( wsClientOptions, - getSilentLogger('expectSuccess') + getSilentLogger('expectSuccess'), ); wsClient.connectAll(); logAllEvents(wsClient); diff --git a/test/ws.util.ts b/test/ws.util.ts index ad509e7..1a83175 100644 --- a/test/ws.util.ts +++ b/test/ws.util.ts @@ -24,12 +24,12 @@ export const fullLogger = { export function waitForSocketEvent( wsClient: WebsocketClient, event: WsClientEvent, - timeoutMs: number = 4.5 * 1000 + timeoutMs: number = 4.5 * 1000, ) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject( - `Failed to receive "${event}" event before timeout. Check that these are correct: topic, api keys (if private), signature process (if private)` + `Failed to receive "${event}" event before timeout. Check that these are correct: topic, api keys (if private), signature process (if private)`, ); }, timeoutMs);