Merge pull request #196 from tiagosiebler/next
v3.2.0: Contract V3 REST & WebSocket Clients. Improve websocket reconnection resilience
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
Node.js connector for the Bybit APIs and WebSockets:
|
||||
- Complete integration with all bybit APIs.
|
||||
- TypeScript support (with type declarations for most API requests & responses).
|
||||
- Over 300 integration tests making real API calls & WebSocket connections, validating any changes before they reach npm.
|
||||
- Over 300 end-to-end tests making real API calls & WebSocket connections, validating any changes before they reach npm.
|
||||
- Robust WebSocket integration with configurable connection heartbeats & automatic reconnect then resubscribe workflows.
|
||||
- Browser support (via webpack bundle - see "Browser Usage" below).
|
||||
|
||||
@@ -25,10 +25,10 @@ Node.js connector for the Bybit APIs and WebSockets:
|
||||
## Related projects
|
||||
Check out my related projects:
|
||||
- Try my connectors:
|
||||
- [ftx-api](https://www.npmjs.com/package/ftx-api)
|
||||
- [bybit-api](https://www.npmjs.com/package/bybit-api)
|
||||
- [binance](https://www.npmjs.com/package/binance)
|
||||
- [bybit-api](https://www.npmjs.com/package/bybit-api)
|
||||
- [okx-api](https://www.npmjs.com/package/okx-api)
|
||||
- [ftx-api](https://www.npmjs.com/package/ftx-api)
|
||||
- Try my misc utilities:
|
||||
- [orderbooks](https://www.npmjs.com/package/orderbooks)
|
||||
- Check out my examples:
|
||||
@@ -180,6 +180,8 @@ The WebsocketClient can be configured to a specific API group using the market p
|
||||
| Copy Trading | `market: 'linear'` | The [copy trading](https://bybit-exchange.github.io/docs/copy_trading/#t-websocket) category. Use the linear market to listen to all copy trading topics. |
|
||||
| USDC Perps | `market: 'usdcPerp` | The [USDC perps](https://bybit-exchange.github.io/docs/usdc/perpetual/#t-websocket) category. |
|
||||
| USDC Options | `market: 'usdcOption'`| The [USDC options](https://bybit-exchange.github.io/docs/usdc/option/#t-websocket) category. |
|
||||
| Contract v3 USDT | `market: 'contractUSDT'`| The [Contract V3](https://bybit-exchange.github.io/docs/derivativesV3/contract/#t-websocket) category (USDT perps) |
|
||||
| Contract v3 Inverse | `market: 'contractInverse'`| The [Contract V3](https://bybit-exchange.github.io/docs/derivativesV3/contract/#t-websocket) category (inverse perps) |
|
||||
|
||||
```javascript
|
||||
const { WebsocketClient } = require('bybit-api');
|
||||
|
||||
24
examples/rest-contract-private.ts
Normal file
24
examples/rest-contract-private.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ContractClient } from '../src/index';
|
||||
|
||||
// or
|
||||
// import { ContractClient } from 'bybit-api';
|
||||
|
||||
const key = process.env.API_KEY_COM;
|
||||
const secret = process.env.API_SECRET_COM;
|
||||
|
||||
const client = new ContractClient({
|
||||
key,
|
||||
secret,
|
||||
strict_param_validation: true,
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const getPositions = await client.getPositions({
|
||||
settleCoin: 'USDT',
|
||||
});
|
||||
console.log('getPositions:', getPositions);
|
||||
} catch (e) {
|
||||
console.error('request failed: ', e);
|
||||
}
|
||||
})();
|
||||
@@ -13,10 +13,13 @@ import { WebsocketClient, WS_KEY_MAP, DefaultLogger } from '../src';
|
||||
const secret = process.env.API_SECRET;
|
||||
|
||||
// USDT Perps:
|
||||
const market = 'linear';
|
||||
// const market = 'linear';
|
||||
// Inverse Perp
|
||||
// const market = 'inverse';
|
||||
// const market = 'spotv3';
|
||||
// Contract v3
|
||||
const market = 'contractUSDT';
|
||||
// const market = 'contractInverse';
|
||||
|
||||
// Note: the WebsocketClient defaults to testnet. Set `livenet: true` to use live markets.
|
||||
const wsClient = new WebsocketClient(
|
||||
@@ -48,8 +51,19 @@ import { WebsocketClient, WS_KEY_MAP, DefaultLogger } from '../src';
|
||||
wsClient.on('reconnected', (data) => {
|
||||
console.log('ws has reconnected ', data?.wsKey);
|
||||
});
|
||||
wsClient.on('error', (data) => {
|
||||
console.error('ws exception: ', data);
|
||||
});
|
||||
|
||||
// subscribe to private endpoints
|
||||
// check the api docs in your api category to see the available topics
|
||||
wsClient.subscribe(['position', 'execution', 'order', 'wallet']);
|
||||
// wsClient.subscribe(['position', 'execution', 'order', 'wallet']);
|
||||
|
||||
// Contract v3
|
||||
wsClient.subscribe([
|
||||
'user.position.contractAccount',
|
||||
'user.execution.contractAccount',
|
||||
'user.order.contractAccount',
|
||||
'user.wallet.contractAccount',
|
||||
]);
|
||||
})();
|
||||
|
||||
@@ -51,6 +51,9 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src';
|
||||
wsClient.on('reconnected', (data) => {
|
||||
console.log('ws has reconnected ', data?.wsKey);
|
||||
});
|
||||
// wsClient.on('error', (data) => {
|
||||
// console.error('ws exception: ', data);
|
||||
// });
|
||||
|
||||
// Inverse
|
||||
// wsClient.subscribe('trade');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bybit-api",
|
||||
"version": "3.1.3",
|
||||
"version": "3.2.0",
|
||||
"description": "Complete & robust node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
||||
@@ -54,6 +54,12 @@ export const API_ERROR_CODE = {
|
||||
INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080,
|
||||
SAME_SLTP_MODE_LINEAR: 130150,
|
||||
RISK_ID_NOT_MODIFIED: 134026,
|
||||
CONTRACT_ORDER_NOT_EXISTS: 140001,
|
||||
CONTRACT_INSUFFICIENT_BALANCE: 140007,
|
||||
CONTRACT_POSITION_MODE_NOT_MODIFIED: 140025,
|
||||
CONTRACT_MARGIN_MODE_NOT_MODIFIED: 140026,
|
||||
CONTRACT_RISK_LIMIT_INFO_NOT_EXISTS: 140031,
|
||||
CONTRACT_SET_LEVERAGE_NOT_MODIFIED: 140043,
|
||||
/** E.g. USDC Options trading, trying to access a symbol that is no longer active */
|
||||
CONTRACT_NAME_NOT_EXIST: 3100111,
|
||||
ORDER_NOT_EXIST: 3100136,
|
||||
|
||||
341
src/contract-client.ts
Normal file
341
src/contract-client.ts
Normal file
@@ -0,0 +1,341 @@
|
||||
import {
|
||||
APIResponseWithTime,
|
||||
APIResponseV3,
|
||||
UMCandlesRequest,
|
||||
UMCategory,
|
||||
UMFundingRateHistoryRequest,
|
||||
UMInstrumentInfoRequest,
|
||||
UMOpenInterestRequest,
|
||||
UMOptionDeliveryPriceRequest,
|
||||
UMPublicTradesRequest,
|
||||
ContractOrderRequest,
|
||||
ContractHistoricOrdersRequest,
|
||||
ContractCancelOrderRequest,
|
||||
ContractModifyOrderRequest,
|
||||
ContractActiveOrdersRequest,
|
||||
ContractPositionsRequest,
|
||||
ContractSetAutoAddMarginRequest,
|
||||
ContractSetMarginSwitchRequest,
|
||||
ContractSetPositionModeRequest,
|
||||
ContractSetTPSLRequest,
|
||||
ContractUserExecutionHistoryRequest,
|
||||
ContractClosedPNLRequest,
|
||||
ContractWalletFundRecordRequest,
|
||||
} from './types';
|
||||
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
|
||||
/**
|
||||
* REST API client for Derivatives V3 Contract APIs
|
||||
*/
|
||||
export class ContractClient extends BaseRestClient {
|
||||
getClientType() {
|
||||
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
|
||||
return REST_CLIENT_TYPE_ENUM.v3;
|
||||
}
|
||||
|
||||
async fetchServerTime(): Promise<number> {
|
||||
const res = await this.getServerTime();
|
||||
return Number(res.time_now);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints : these seem exactly the same as the unified margin market data endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
/** Query order book info. Each side has a depth of 25 orders. */
|
||||
getOrderBook(
|
||||
symbol: string,
|
||||
category: string,
|
||||
limit?: number
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/order-book/L2', {
|
||||
category,
|
||||
symbol,
|
||||
limit,
|
||||
});
|
||||
}
|
||||
|
||||
/** Get candles/klines */
|
||||
getCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/kline', params);
|
||||
}
|
||||
|
||||
/** Get a symbol price/statistics ticker */
|
||||
getSymbolTicker(
|
||||
category: UMCategory,
|
||||
symbol?: string
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/tickers', { category, symbol });
|
||||
}
|
||||
|
||||
/** Get trading rules per symbol/contract, incl price/amount/value/leverage filters */
|
||||
getInstrumentInfo(
|
||||
params: UMInstrumentInfoRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/instruments-info', params);
|
||||
}
|
||||
|
||||
/** Query mark price kline (like getCandles() but for mark price). */
|
||||
getMarkPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/mark-price-kline', params);
|
||||
}
|
||||
|
||||
/** Query Index Price Kline */
|
||||
getIndexPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/index-price-kline', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* The funding rate is generated every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC.
|
||||
* For example, if a request is sent at 12:00 UTC, the funding rate generated earlier that day at 08:00 UTC will be sent.
|
||||
*/
|
||||
getFundingRateHistory(
|
||||
params: UMFundingRateHistoryRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get(
|
||||
'/derivatives/v3/public/funding/history-funding-rate',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/** Get Risk Limit */
|
||||
getRiskLimit(
|
||||
category: UMCategory,
|
||||
symbol: string
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/risk-limit/list', {
|
||||
category,
|
||||
symbol,
|
||||
});
|
||||
}
|
||||
|
||||
/** Get option delivery price */
|
||||
getOptionDeliveryPrice(
|
||||
params: UMOptionDeliveryPriceRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/delivery-price', params);
|
||||
}
|
||||
|
||||
/** Get public trading history */
|
||||
getTrades(params: UMPublicTradesRequest): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/recent-trade', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total amount of unsettled contracts.
|
||||
* In other words, the total number of contracts held in open positions.
|
||||
*/
|
||||
getOpenInterest(params: UMOpenInterestRequest): Promise<APIResponseV3<any>> {
|
||||
return this.get('/derivatives/v3/public/open-interest', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Contract Account Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
/** -> Order API */
|
||||
|
||||
/** Place an order */
|
||||
submitOrder(params: ContractOrderRequest): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/order/create', params);
|
||||
}
|
||||
|
||||
/** Query order history. As order creation/cancellation is asynchronous, the data returned from the interface may be delayed. To access order information in real-time, call getActiveOrders() */
|
||||
getHistoricOrders(
|
||||
params: ContractHistoricOrdersRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/order/list', params);
|
||||
}
|
||||
|
||||
/** Cancel order */
|
||||
cancelOrder(params: ContractCancelOrderRequest): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/order/cancel', params);
|
||||
}
|
||||
|
||||
/** Cancel all orders */
|
||||
cancelAllOrders(symbol: string): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/order/cancel-all', {
|
||||
symbol,
|
||||
});
|
||||
}
|
||||
|
||||
/** Replace order : Active order parameters (such as quantity, price) and stop order parameters cannot be modified in one request at the same time. Please request modification separately. */
|
||||
modifyOrder(params: ContractModifyOrderRequest): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/order/replace', params);
|
||||
}
|
||||
|
||||
/** Query Open Order(s) (real-time) */
|
||||
getActiveOrders(
|
||||
params: ContractActiveOrdersRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate(
|
||||
'/contract/v3/private/order/unfilled-orders',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/** -> Positions API */
|
||||
|
||||
/**
|
||||
* Query my positions real-time. Accessing personal list of positions.
|
||||
* Either symbol or settleCoin is required.
|
||||
* Users can access their position holding information through this interface, such as the number of position holdings and wallet balance.
|
||||
*/
|
||||
getPositions(params?: ContractPositionsRequest): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/position/list', params);
|
||||
}
|
||||
|
||||
/** Set auto add margin, or Auto-Margin Replenishment. */
|
||||
setAutoAddMargin(
|
||||
params: ContractSetAutoAddMarginRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate(
|
||||
'/contract/v3/private/position/set-auto-add-margin',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/** Switch cross margin mode/isolated margin mode */
|
||||
setMarginSwitch(
|
||||
params: ContractSetMarginSwitchRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate(
|
||||
'/contract/v3/private/position/switch-isolated',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/** Supports switching between One-Way Mode and Hedge Mode at the coin level. */
|
||||
setPositionMode(
|
||||
params: ContractSetPositionModeRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate(
|
||||
'/contract/v3/private/position/switch-mode',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch mode between Full or Partial
|
||||
*/
|
||||
setTPSLMode(
|
||||
symbol: string,
|
||||
tpSlMode: 'Full' | 'Partial'
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/position/switch-tpsl-mode', {
|
||||
symbol,
|
||||
tpSlMode,
|
||||
});
|
||||
}
|
||||
|
||||
/** Leverage setting. */
|
||||
setLeverage(
|
||||
symbol: string,
|
||||
buyLeverage: string,
|
||||
sellLeverage: string
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/position/set-leverage', {
|
||||
symbol,
|
||||
buyLeverage,
|
||||
sellLeverage,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set take profit, stop loss, and trailing stop for your open position.
|
||||
* If using partial mode, TP/SL/TS orders will not close your entire position.
|
||||
*/
|
||||
setTPSL(params: ContractSetTPSLRequest): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate(
|
||||
'/contract/v3/private/position/trading-stop',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/** Set risk limit */
|
||||
setRiskLimit(
|
||||
symbol: string,
|
||||
riskId: number,
|
||||
/** 0-one-way, 1-buy side, 2-sell side */
|
||||
positionIdx: 0 | 1 | 2
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.postPrivate('/contract/v3/private/position/set-risk-limit', {
|
||||
symbol,
|
||||
riskId,
|
||||
positionIdx,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's trading records.
|
||||
* The results are ordered in descending order (the first item is the latest). Returns records up to 2 years old.
|
||||
*/
|
||||
getUserExecutionHistory(
|
||||
params: ContractUserExecutionHistoryRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/execution/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's closed profit and loss records.
|
||||
* The results are ordered in descending order (the first item is the latest).
|
||||
*/
|
||||
getClosedProfitAndLoss(
|
||||
params: ContractClosedPNLRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/position/closed-pnl', params);
|
||||
}
|
||||
|
||||
/** Get the information of open interest limit. */
|
||||
getOpenInterestLimitInfo(symbol: string): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/position/closed-pnl', {
|
||||
symbol,
|
||||
});
|
||||
}
|
||||
|
||||
/** -> Account API */
|
||||
|
||||
/** Query wallet balance */
|
||||
getBalances(coin?: string): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/account/wallet/balance', {
|
||||
coin,
|
||||
});
|
||||
}
|
||||
|
||||
/** Get user trading fee rate */
|
||||
getTradingFeeRate(symbol?: string): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate('/contract/v3/private/account/fee-rate', {
|
||||
symbol,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wallet fund records.
|
||||
* This endpoint also shows exchanges from the Asset Exchange, where the types for the exchange are ExchangeOrderWithdraw and ExchangeOrderDeposit.
|
||||
* This endpoint returns incomplete information for transfers involving the derivatives wallet.
|
||||
* Use the account asset API for creating and querying internal transfers.
|
||||
*/
|
||||
getWalletFundRecords(
|
||||
params?: ContractWalletFundRecordRequest
|
||||
): Promise<APIResponseV3<any>> {
|
||||
return this.getPrivate(
|
||||
'/contract/v3/private/account/wallet/fund-records',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* API Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getServerTime(): Promise<APIResponseWithTime> {
|
||||
return this.get('/v2/public/time');
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export * from './spot-client-v3';
|
||||
export * from './usdc-option-client';
|
||||
export * from './usdc-perpetual-client';
|
||||
export * from './unified-margin-client';
|
||||
export * from './contract-client';
|
||||
export * from './websocket-client';
|
||||
export * from './util/logger';
|
||||
export * from './util';
|
||||
|
||||
128
src/types/request/contract.ts
Normal file
128
src/types/request/contract.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { OrderSide } from '../shared';
|
||||
import { UMOrderType } from './unified-margin';
|
||||
import { USDCOrderFilter, USDCTimeInForce } from './usdc-shared';
|
||||
|
||||
export interface ContractOrderRequest {
|
||||
symbol: string;
|
||||
side: OrderSide;
|
||||
positionIdx?: '0' | '1' | '2';
|
||||
orderType: UMOrderType;
|
||||
qty: string;
|
||||
price?: string;
|
||||
triggerDirection?: '1' | '2';
|
||||
triggerPrice?: string;
|
||||
triggerBy?: string;
|
||||
tpTriggerBy?: string;
|
||||
slTriggerBy?: string;
|
||||
timeInForce: USDCTimeInForce;
|
||||
orderLinkId?: string;
|
||||
takeProfit?: number;
|
||||
stopLoss?: number;
|
||||
reduceOnly?: boolean;
|
||||
closeOnTrigger?: boolean;
|
||||
}
|
||||
|
||||
export interface ContractHistoricOrdersRequest {
|
||||
orderId?: string;
|
||||
orderLinkId?: string;
|
||||
symbol: string;
|
||||
orderStatus?: string;
|
||||
orderFilter?: USDCOrderFilter;
|
||||
limit?: number;
|
||||
cursor?: string;
|
||||
}
|
||||
|
||||
export interface ContractCancelOrderRequest {
|
||||
symbol: string;
|
||||
orderId?: string;
|
||||
orderLinkId?: string;
|
||||
}
|
||||
|
||||
export interface ContractModifyOrderRequest {
|
||||
orderId?: string;
|
||||
orderLinkId?: string;
|
||||
symbol: string;
|
||||
qty?: string;
|
||||
price?: string;
|
||||
takeProfit?: number;
|
||||
stopLoss?: number;
|
||||
tpTriggerBy?: string;
|
||||
slTriggerBy?: string;
|
||||
triggerBy?: string;
|
||||
}
|
||||
|
||||
export interface ContractActiveOrdersRequest {
|
||||
symbol?: string;
|
||||
orderId?: string;
|
||||
orderLinkId?: string;
|
||||
settleCoin?: string;
|
||||
orderFilter?: USDCOrderFilter;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface ContractPositionsRequest {
|
||||
symbol?: string;
|
||||
settleCoin?: string;
|
||||
dataFilter?: string;
|
||||
}
|
||||
|
||||
export interface ContractSetAutoAddMarginRequest {
|
||||
symbol: string;
|
||||
side: 'Buy' | 'Sell';
|
||||
autoAddMargin: 1 | 0;
|
||||
positionIdx?: 0 | 1 | 2;
|
||||
}
|
||||
|
||||
export interface ContractSetMarginSwitchRequest {
|
||||
symbol: string;
|
||||
tradeMode: 0 | 1;
|
||||
buyLeverage: string;
|
||||
sellLeverage: string;
|
||||
}
|
||||
|
||||
export interface ContractSetPositionModeRequest {
|
||||
symbol?: string;
|
||||
coin?: string;
|
||||
mode: 0 | 3;
|
||||
}
|
||||
|
||||
export interface ContractSetTPSLRequest {
|
||||
symbol: string;
|
||||
takeProfit?: string;
|
||||
stopLoss?: string;
|
||||
activePrice?: string;
|
||||
trailingStop?: string;
|
||||
tpTriggerBy?: string;
|
||||
slTriggerBy?: string;
|
||||
slSize?: string;
|
||||
tpSize?: string;
|
||||
/** 0-one-way, 1-buy side, 2-sell side */
|
||||
positionIdx?: 0 | 1 | 2;
|
||||
}
|
||||
|
||||
export interface ContractUserExecutionHistoryRequest {
|
||||
symbol: string;
|
||||
orderId?: string;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
execType?: 'Trade' | 'AdlTrade' | 'Funding' | 'BustTrade';
|
||||
limit?: number;
|
||||
cursor?: string;
|
||||
}
|
||||
|
||||
export interface ContractClosedPNLRequest {
|
||||
symbol: string;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
limit?: number;
|
||||
cursor?: string;
|
||||
}
|
||||
|
||||
export interface ContractWalletFundRecordRequest {
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
coin?: string;
|
||||
walletFundType?: string;
|
||||
limit?: string;
|
||||
cursor?: string;
|
||||
}
|
||||
@@ -7,3 +7,4 @@ export * from './usdc-perp';
|
||||
export * from './usdc-options';
|
||||
export * from './usdc-shared';
|
||||
export * from './unified-margin';
|
||||
export * from './contract';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ContractClient } from '../contract-client';
|
||||
import { InverseClient } from '../inverse-client';
|
||||
import { LinearClient } from '../linear-client';
|
||||
import { SpotClient } from '../spot-client';
|
||||
@@ -13,7 +14,8 @@ export type RESTClient =
|
||||
| SpotClientV3
|
||||
| USDCOptionClient
|
||||
| USDCPerpetualClient
|
||||
| UnifiedMarginClient;
|
||||
| UnifiedMarginClient
|
||||
| ContractClient;
|
||||
|
||||
export type numberInString = string;
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ export type APIMarket =
|
||||
| 'usdcOption'
|
||||
| 'usdcPerp'
|
||||
| 'unifiedPerp'
|
||||
| 'unifiedOption';
|
||||
| 'unifiedOption'
|
||||
| 'contractUSDT'
|
||||
| 'contractInverse';
|
||||
|
||||
// Same as inverse futures
|
||||
export type WsPublicInverseTopic =
|
||||
|
||||
@@ -25,6 +25,7 @@ import BaseRestClient from './util/BaseRestClient';
|
||||
*/
|
||||
export class USDCPerpetualClient extends BaseRestClient {
|
||||
getClientType() {
|
||||
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
|
||||
return REST_CLIENT_TYPE_ENUM.v3;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,26 @@ export const WS_BASE_URL_MAP: Record<
|
||||
testnet: 'useUnifiedEndpoint',
|
||||
},
|
||||
},
|
||||
contractUSDT: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/contract/usdt/public/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3',
|
||||
},
|
||||
private: {
|
||||
livenet: 'wss://stream.bybit.com/contract/private/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/private/v3',
|
||||
},
|
||||
},
|
||||
contractInverse: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/contract/inverse/public/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/inverse/public/v3',
|
||||
},
|
||||
private: {
|
||||
livenet: 'wss://stream.bybit.com/contract/private/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/private/v3',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WS_KEY_MAP = {
|
||||
@@ -139,6 +159,10 @@ export const WS_KEY_MAP = {
|
||||
unifiedOptionPublic: 'unifiedOptionPublic',
|
||||
unifiedPerpUSDTPublic: 'unifiedPerpUSDTPublic',
|
||||
unifiedPerpUSDCPublic: 'unifiedPerpUSDCPublic',
|
||||
contractUSDTPublic: 'contractUSDTPublic',
|
||||
contractUSDTPrivate: 'contractUSDTPrivate',
|
||||
contractInversePublic: 'contractInversePublic',
|
||||
contractInversePrivate: 'contractInversePrivate',
|
||||
} as const;
|
||||
|
||||
export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
|
||||
@@ -146,6 +170,8 @@ export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
|
||||
WS_KEY_MAP.usdcOptionPrivate,
|
||||
WS_KEY_MAP.usdcPerpPrivate,
|
||||
WS_KEY_MAP.unifiedPrivate,
|
||||
WS_KEY_MAP.contractUSDTPrivate,
|
||||
WS_KEY_MAP.contractInversePrivate,
|
||||
];
|
||||
|
||||
export const PUBLIC_WS_KEYS = [
|
||||
@@ -157,6 +183,8 @@ export const PUBLIC_WS_KEYS = [
|
||||
WS_KEY_MAP.unifiedOptionPublic,
|
||||
WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||
WS_KEY_MAP.unifiedPerpUSDCPublic,
|
||||
WS_KEY_MAP.contractUSDTPublic,
|
||||
WS_KEY_MAP.contractInversePublic,
|
||||
] as string[];
|
||||
|
||||
/** Used to automatically determine if a sub request should be to the public or private ws (when there's two) */
|
||||
@@ -193,6 +221,11 @@ const PRIVATE_TOPICS = [
|
||||
'user.order.unifiedAccount',
|
||||
'user.wallet.unifiedAccount',
|
||||
'user.greeks.unifiedAccount',
|
||||
// contract v3
|
||||
'user.position.contractAccount',
|
||||
'user.execution.contractAccount',
|
||||
'user.order.contractAccount',
|
||||
'user.wallet.contractAccount',
|
||||
];
|
||||
|
||||
export function getWsKeyForTopic(
|
||||
@@ -251,6 +284,16 @@ export function getWsKeyForTopic(
|
||||
`Failed to determine wskey for unified perps topic: "${topic}`
|
||||
);
|
||||
}
|
||||
case 'contractInverse': {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.contractInversePrivate
|
||||
: WS_KEY_MAP.contractInversePublic;
|
||||
}
|
||||
case 'contractUSDT': {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.contractUSDTPrivate
|
||||
: WS_KEY_MAP.contractUSDTPublic;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(market, `getWsKeyForTopic(): Unhandled market`);
|
||||
}
|
||||
@@ -267,7 +310,9 @@ export function getMaxTopicsPerSubscribeEvent(
|
||||
case 'usdcPerp':
|
||||
case 'unifiedOption':
|
||||
case 'unifiedPerp':
|
||||
case 'spot': {
|
||||
case 'spot':
|
||||
case 'contractInverse':
|
||||
case 'contractUSDT': {
|
||||
return null;
|
||||
}
|
||||
case 'spotv3': {
|
||||
|
||||
@@ -5,6 +5,10 @@ import { InverseClient } from './inverse-client';
|
||||
import { LinearClient } from './linear-client';
|
||||
import { SpotClientV3 } from './spot-client-v3';
|
||||
import { SpotClient } from './spot-client';
|
||||
import { USDCOptionClient } from './usdc-option-client';
|
||||
import { USDCPerpetualClient } from './usdc-perpetual-client';
|
||||
import { UnifiedMarginClient } from './unified-margin-client';
|
||||
import { ContractClient } from './contract-client';
|
||||
|
||||
import { signMessage } from './util/node-support';
|
||||
import WsStore from './util/WsStore';
|
||||
@@ -32,9 +36,6 @@ import {
|
||||
neverGuard,
|
||||
getMaxTopicsPerSubscribeEvent,
|
||||
} from './util';
|
||||
import { USDCOptionClient } from './usdc-option-client';
|
||||
import { USDCPerpetualClient } from './usdc-perpetual-client';
|
||||
import { UnifiedMarginClient } from './unified-margin-client';
|
||||
|
||||
const loggerCategory = { category: 'bybit-ws' };
|
||||
|
||||
@@ -106,6 +107,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
};
|
||||
|
||||
this.prepareRESTClient();
|
||||
|
||||
// add default error handling so this doesn't crash node (if the user didn't set a handler)
|
||||
this.on('error', () => {});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,6 +236,14 @@ export class WebsocketClient extends EventEmitter {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'contractInverse':
|
||||
case 'contractUSDT': {
|
||||
this.restClient = new ContractClient(
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -264,6 +276,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
public closeAll(force?: boolean) {
|
||||
const keys = this.wsStore.getKeys();
|
||||
this.logger.info(`Closing all ws connections: ${keys}`);
|
||||
keys.forEach((key) => {
|
||||
this.close(key, force);
|
||||
});
|
||||
@@ -285,7 +298,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'usdcOption':
|
||||
case 'usdcPerp':
|
||||
case 'unifiedPerp':
|
||||
case 'unifiedOption': {
|
||||
case 'unifiedOption':
|
||||
case 'contractUSDT':
|
||||
case 'contractInverse': {
|
||||
return [...this.connectPublic(), this.connectPrivate()];
|
||||
}
|
||||
default: {
|
||||
@@ -323,6 +338,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.connect(WS_KEY_MAP.unifiedPerpUSDCPublic),
|
||||
];
|
||||
}
|
||||
case 'contractUSDT':
|
||||
return [this.connect(WS_KEY_MAP.contractUSDTPublic)];
|
||||
case 'contractInverse':
|
||||
return [this.connect(WS_KEY_MAP.contractInversePublic)];
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -356,6 +375,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'unifiedOption': {
|
||||
return this.connect(WS_KEY_MAP.unifiedPrivate);
|
||||
}
|
||||
case 'contractUSDT':
|
||||
return this.connect(WS_KEY_MAP.contractUSDTPrivate);
|
||||
case 'contractInverse':
|
||||
return this.connect(WS_KEY_MAP.contractInversePrivate);
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -399,7 +422,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
return this.wsStore.setWs(wsKey, ws);
|
||||
} catch (err) {
|
||||
this.parseWsError('Connection failed', err, wsKey);
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,12 +442,22 @@ export class WebsocketClient extends EventEmitter {
|
||||
break;
|
||||
|
||||
default:
|
||||
this.logger.error(
|
||||
`${context} due to unexpected response error: "${
|
||||
error?.msg || error?.message || error
|
||||
}"`,
|
||||
{ ...loggerCategory, wsKey, error }
|
||||
);
|
||||
if (
|
||||
this.wsStore.getConnectionState(wsKey) !==
|
||||
WsConnectionStateEnum.CLOSING
|
||||
) {
|
||||
this.logger.error(
|
||||
`${context} due to unexpected response error: "${
|
||||
error?.msg || error?.message || error
|
||||
}"`,
|
||||
{ ...loggerCategory, wsKey, error }
|
||||
);
|
||||
this.executeReconnectableClose(wsKey, 'unhandled onWsError');
|
||||
} else {
|
||||
this.logger.info(
|
||||
`${wsKey} socket forcefully closed. Will not reconnect.`
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.emit('error', error);
|
||||
@@ -518,11 +551,16 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.setWsState(wsKey, WsConnectionStateEnum.RECONNECTING);
|
||||
}
|
||||
|
||||
if (this.wsStore.get(wsKey)?.activeReconnectTimer) {
|
||||
this.clearReconnectTimer(wsKey);
|
||||
}
|
||||
|
||||
this.wsStore.get(wsKey, true).activeReconnectTimer = setTimeout(() => {
|
||||
this.logger.info('Reconnecting to websocket', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
this.clearReconnectTimer(wsKey);
|
||||
this.connect(wsKey);
|
||||
}, connectionDelayMs);
|
||||
}
|
||||
@@ -537,23 +575,47 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.logger.silly('Sending ping', { ...loggerCategory, wsKey });
|
||||
this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' }));
|
||||
|
||||
this.wsStore.get(wsKey, true).activePongTimer = setTimeout(() => {
|
||||
this.logger.info('Pong timeout - closing socket to reconnect', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
this.getWs(wsKey)?.terminate();
|
||||
delete this.wsStore.get(wsKey, true).activePongTimer;
|
||||
}, this.options.pongTimeout);
|
||||
this.wsStore.get(wsKey, true).activePongTimer = setTimeout(
|
||||
() => this.executeReconnectableClose(wsKey, 'Pong timeout'),
|
||||
this.options.pongTimeout
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a connection, if it's even open. If open, this will trigger a reconnect asynchronously.
|
||||
* If closed, trigger a reconnect immediately
|
||||
*/
|
||||
private executeReconnectableClose(wsKey: WsKey, reason: string) {
|
||||
this.logger.info(`${reason} - closing socket to reconnect`, {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
reason,
|
||||
});
|
||||
|
||||
const wasOpen = this.wsStore.isWsOpen(wsKey);
|
||||
|
||||
this.getWs(wsKey)?.terminate();
|
||||
delete this.wsStore.get(wsKey, true).activePongTimer;
|
||||
this.clearPingTimer(wsKey);
|
||||
this.clearPongTimer(wsKey);
|
||||
|
||||
if (!wasOpen) {
|
||||
this.logger.info(
|
||||
`${reason} - socket already closed - trigger immediate reconnect`,
|
||||
{
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
reason,
|
||||
}
|
||||
);
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
private clearTimers(wsKey: WsKey) {
|
||||
this.clearPingTimer(wsKey);
|
||||
this.clearPongTimer(wsKey);
|
||||
const wsState = this.wsStore.get(wsKey);
|
||||
if (wsState?.activeReconnectTimer) {
|
||||
clearTimeout(wsState.activeReconnectTimer);
|
||||
}
|
||||
this.clearReconnectTimer(wsKey);
|
||||
}
|
||||
|
||||
// Send a ping at intervals
|
||||
@@ -574,6 +636,14 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private clearReconnectTimer(wsKey: WsKey) {
|
||||
const wsState = this.wsStore.get(wsKey);
|
||||
if (wsState?.activeReconnectTimer) {
|
||||
clearTimeout(wsState.activeReconnectTimer);
|
||||
wsState.activeReconnectTimer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private Use the `subscribe(topics)` method to subscribe to topics. Send WS message to subscribe to topics.
|
||||
*/
|
||||
@@ -682,7 +752,8 @@ export class WebsocketClient extends EventEmitter {
|
||||
const ws = new WebSocket(url, undefined, agent ? { agent } : undefined);
|
||||
ws.onopen = (event) => this.onWsOpen(event, wsKey);
|
||||
ws.onmessage = (event) => this.onWsMessage(event, wsKey);
|
||||
ws.onerror = (event) => this.onWsError(event, wsKey);
|
||||
ws.onerror = (event) =>
|
||||
this.parseWsError('Websocket onWsError', event, wsKey);
|
||||
ws.onclose = (event) => this.onWsClose(event, wsKey);
|
||||
|
||||
return ws;
|
||||
@@ -781,10 +852,6 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private onWsError(error: any, wsKey: WsKey) {
|
||||
this.parseWsError('Websocket error', error, wsKey);
|
||||
}
|
||||
|
||||
private onWsClose(event, wsKey: WsKey) {
|
||||
this.logger.info('Websocket connection closed', {
|
||||
...loggerCategory,
|
||||
@@ -794,7 +861,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
if (
|
||||
this.wsStore.getConnectionState(wsKey) !== WsConnectionStateEnum.CLOSING
|
||||
) {
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
|
||||
this.emit('reconnect', { wsKey, event });
|
||||
} else {
|
||||
this.setWsState(wsKey, WsConnectionStateEnum.INITIAL);
|
||||
@@ -864,6 +931,18 @@ export class WebsocketClient extends EventEmitter {
|
||||
case WS_KEY_MAP.unifiedPrivate: {
|
||||
return WS_BASE_URL_MAP.unifiedPerp.private[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.contractInversePrivate: {
|
||||
return WS_BASE_URL_MAP.contractInverse.private[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.contractInversePublic: {
|
||||
return WS_BASE_URL_MAP.contractInverse.public[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.contractUSDTPrivate: {
|
||||
return WS_BASE_URL_MAP.contractUSDT.private[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.contractUSDTPublic: {
|
||||
return WS_BASE_URL_MAP.contractUSDT.public[networkKey];
|
||||
}
|
||||
default: {
|
||||
this.logger.error('getWsUrl(): Unhandled wsKey: ', {
|
||||
...loggerCategory,
|
||||
|
||||
71
test/contract/private.read.test.ts
Normal file
71
test/contract/private.read.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { API_ERROR_CODE, ContractClient } from '../../src';
|
||||
import { successResponseObjectV3 } from '../response.util';
|
||||
|
||||
describe('Private Contract REST API GET Endpoints', () => {
|
||||
const API_KEY = process.env.API_KEY_COM;
|
||||
const API_SECRET = process.env.API_SECRET_COM;
|
||||
|
||||
it('should have api credentials to test with', () => {
|
||||
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
const api = new ContractClient({
|
||||
key: API_KEY,
|
||||
secret: API_SECRET,
|
||||
testnet: false,
|
||||
});
|
||||
|
||||
const symbol = 'BTCUSDT';
|
||||
it('getHistoricOrders()', async () => {
|
||||
expect(await api.getHistoricOrders({ symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getActiveOrders()', async () => {
|
||||
expect(await api.getActiveOrders({ symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getPositions()', async () => {
|
||||
expect(await api.getPositions({ symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getUserExecutionHistory()', async () => {
|
||||
expect(await api.getUserExecutionHistory({ symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getClosedProfitAndLoss()', async () => {
|
||||
expect(await api.getClosedProfitAndLoss({ symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getOpenInterestLimitInfo()', async () => {
|
||||
expect(await api.getOpenInterestLimitInfo(symbol)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getBalances()', async () => {
|
||||
expect(await api.getBalances()).toMatchObject(successResponseObjectV3());
|
||||
});
|
||||
|
||||
it('getTradingFeeRate()', async () => {
|
||||
expect(await api.getTradingFeeRate()).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getWalletFundRecords()', async () => {
|
||||
expect(await api.getWalletFundRecords()).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
});
|
||||
139
test/contract/private.write.test.ts
Normal file
139
test/contract/private.write.test.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { API_ERROR_CODE, ContractClient } from '../../src';
|
||||
import { successResponseObjectV3 } from '../response.util';
|
||||
|
||||
describe('Private Contract REST API POST Endpoints', () => {
|
||||
const API_KEY = process.env.API_KEY_COM;
|
||||
const API_SECRET = process.env.API_SECRET_COM;
|
||||
|
||||
it('should have api credentials to test with', () => {
|
||||
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
const api = new ContractClient({
|
||||
key: API_KEY,
|
||||
secret: API_SECRET,
|
||||
testnet: false,
|
||||
});
|
||||
|
||||
const symbol = 'BTCUSDT';
|
||||
|
||||
/**
|
||||
* While it may seem silly, these tests simply validate the request is processed at all.
|
||||
* Something very wrong would be a sign error or complaints about the endpoint/request method/server error.
|
||||
*/
|
||||
|
||||
it('submitOrder()', async () => {
|
||||
expect(
|
||||
await api.submitOrder({
|
||||
symbol,
|
||||
side: 'Sell',
|
||||
orderType: 'Limit',
|
||||
qty: '1',
|
||||
price: '20000',
|
||||
orderLinkId: Date.now().toString(),
|
||||
timeInForce: 'GoodTillCancel',
|
||||
positionIdx: '2',
|
||||
})
|
||||
).toMatchObject({
|
||||
// retMsg: '',
|
||||
retCode: API_ERROR_CODE.CONTRACT_INSUFFICIENT_BALANCE,
|
||||
});
|
||||
});
|
||||
|
||||
it('cancelOrder()', async () => {
|
||||
expect(
|
||||
await api.cancelOrder({
|
||||
symbol,
|
||||
orderId: 'somethingFake1',
|
||||
})
|
||||
).toMatchObject({
|
||||
retCode: API_ERROR_CODE.CONTRACT_ORDER_NOT_EXISTS,
|
||||
});
|
||||
});
|
||||
|
||||
it('cancelAllOrders()', async () => {
|
||||
expect(await api.cancelAllOrders(symbol)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('modifyOrder()', async () => {
|
||||
expect(
|
||||
await api.modifyOrder({
|
||||
symbol,
|
||||
orderId: 'somethingFake',
|
||||
price: '20000',
|
||||
})
|
||||
).toMatchObject({
|
||||
retCode: API_ERROR_CODE.CONTRACT_ORDER_NOT_EXISTS,
|
||||
});
|
||||
});
|
||||
|
||||
it('setAutoAddMargin()', async () => {
|
||||
expect(
|
||||
await api.setAutoAddMargin({
|
||||
autoAddMargin: 1,
|
||||
side: 'Buy',
|
||||
symbol,
|
||||
positionIdx: 1,
|
||||
})
|
||||
).toMatchObject({
|
||||
retMsg: expect.stringMatching(/not modified/gim),
|
||||
retCode: API_ERROR_CODE.PARAMS_MISSING_OR_WRONG,
|
||||
});
|
||||
});
|
||||
|
||||
it('setMarginSwitch()', async () => {
|
||||
expect(
|
||||
await api.setMarginSwitch({
|
||||
symbol,
|
||||
tradeMode: 1,
|
||||
buyLeverage: '5',
|
||||
sellLeverage: '5',
|
||||
})
|
||||
).toMatchObject({
|
||||
retCode: API_ERROR_CODE.CONTRACT_MARGIN_MODE_NOT_MODIFIED,
|
||||
});
|
||||
});
|
||||
|
||||
it('setPositionMode()', async () => {
|
||||
expect(
|
||||
await api.setPositionMode({
|
||||
symbol,
|
||||
mode: 3,
|
||||
})
|
||||
).toMatchObject({
|
||||
retCode: API_ERROR_CODE.CONTRACT_POSITION_MODE_NOT_MODIFIED,
|
||||
});
|
||||
});
|
||||
|
||||
it('setTPSLMode()', async () => {
|
||||
expect(await api.setTPSLMode(symbol, 'Full')).toMatchObject({
|
||||
retCode: API_ERROR_CODE.PARAMS_MISSING_OR_WRONG,
|
||||
retMsg: expect.stringMatching(/same/gim),
|
||||
});
|
||||
});
|
||||
|
||||
it('setLeverage()', async () => {
|
||||
expect(await api.setLeverage(symbol, '5', '5')).toMatchObject({
|
||||
retCode: API_ERROR_CODE.CONTRACT_SET_LEVERAGE_NOT_MODIFIED,
|
||||
});
|
||||
});
|
||||
|
||||
it('setTPSL()', async () => {
|
||||
expect(
|
||||
await api.setTPSL({ symbol, positionIdx: 1, stopLoss: '100' })
|
||||
).toMatchObject({
|
||||
retMsg: expect.stringMatching(/zero position/gim),
|
||||
retCode: API_ERROR_CODE.PARAMS_MISSING_OR_WRONG,
|
||||
});
|
||||
});
|
||||
|
||||
it('setRiskLimit()', async () => {
|
||||
expect(await api.setRiskLimit(symbol, 43, 2)).toMatchObject({
|
||||
// retMsg: '',
|
||||
retCode: API_ERROR_CODE.CONTRACT_RISK_LIMIT_INFO_NOT_EXISTS,
|
||||
});
|
||||
});
|
||||
});
|
||||
103
test/contract/public.read.test.ts
Normal file
103
test/contract/public.read.test.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { UMCandlesRequest, ContractClient } from '../../src';
|
||||
import {
|
||||
successResponseObject,
|
||||
successResponseObjectV3,
|
||||
} from '../response.util';
|
||||
|
||||
describe('Public Contract REST API Endpoints', () => {
|
||||
const API_KEY = undefined;
|
||||
const API_SECRET = undefined;
|
||||
|
||||
const api = new ContractClient({
|
||||
key: API_KEY,
|
||||
secret: API_SECRET,
|
||||
testnet: false,
|
||||
});
|
||||
|
||||
const symbol = 'BTCUSDT';
|
||||
const category = 'linear';
|
||||
const start = Number((Date.now() / 1000).toFixed(0));
|
||||
const end = start + 1000 * 60 * 60 * 24;
|
||||
const interval = '1';
|
||||
|
||||
const candleRequest: UMCandlesRequest = {
|
||||
category,
|
||||
symbol,
|
||||
interval,
|
||||
start,
|
||||
end,
|
||||
};
|
||||
|
||||
it('getOrderBook()', async () => {
|
||||
expect(await api.getOrderBook(symbol, category)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getCandles()', async () => {
|
||||
expect(await api.getCandles(candleRequest)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getSymbolTicker()', async () => {
|
||||
expect(await api.getSymbolTicker(category)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getInstrumentInfo()', async () => {
|
||||
expect(await api.getInstrumentInfo({ category })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getMarkPriceCandles()', async () => {
|
||||
expect(await api.getMarkPriceCandles(candleRequest)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getIndexPriceCandles()', async () => {
|
||||
expect(await api.getIndexPriceCandles(candleRequest)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getFundingRateHistory()', async () => {
|
||||
expect(
|
||||
await api.getFundingRateHistory({
|
||||
category,
|
||||
symbol,
|
||||
})
|
||||
).toMatchObject(successResponseObjectV3());
|
||||
});
|
||||
|
||||
it('getRiskLimit()', async () => {
|
||||
expect(await api.getRiskLimit(category, symbol)).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getOptionDeliveryPrice()', async () => {
|
||||
expect(await api.getOptionDeliveryPrice({ category })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getTrades()', async () => {
|
||||
expect(await api.getTrades({ category, symbol })).toMatchObject(
|
||||
successResponseObjectV3()
|
||||
);
|
||||
});
|
||||
|
||||
it('getOpenInterest()', async () => {
|
||||
expect(
|
||||
await api.getOpenInterest({ symbol, category, interval: '5min' })
|
||||
).toMatchObject(successResponseObjectV3());
|
||||
});
|
||||
|
||||
it('getServerTime()', async () => {
|
||||
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||
});
|
||||
});
|
||||
74
test/contract/ws.private.test.ts
Normal file
74
test/contract/ws.private.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
WebsocketClient,
|
||||
WSClientConfigurableOptions,
|
||||
WS_KEY_MAP,
|
||||
} from '../../src';
|
||||
import {
|
||||
logAllEvents,
|
||||
getSilentLogger,
|
||||
waitForSocketEvent,
|
||||
WS_OPEN_EVENT_PARTIAL,
|
||||
fullLogger,
|
||||
} from '../ws.util';
|
||||
|
||||
describe('Private Contract Websocket Client', () => {
|
||||
const API_KEY = process.env.API_KEY_COM;
|
||||
const API_SECRET = process.env.API_SECRET_COM;
|
||||
|
||||
let wsClient: WebsocketClient;
|
||||
|
||||
const wsClientOptions: WSClientConfigurableOptions = {
|
||||
market: 'contractUSDT',
|
||||
key: API_KEY,
|
||||
secret: API_SECRET,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
wsClient = new WebsocketClient(
|
||||
wsClientOptions,
|
||||
getSilentLogger('expectSuccessNoAuth')
|
||||
// fullLogger
|
||||
);
|
||||
// logAllEvents(wsClient);
|
||||
wsClient.connectPrivate();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a private ws connection', async () => {
|
||||
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||
try {
|
||||
expect(await wsOpenPromise).toMatchObject({
|
||||
event: WS_OPEN_EVENT_PARTIAL,
|
||||
wsKey: WS_KEY_MAP.contractUSDTPrivate,
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
});
|
||||
it('should authenticate successfully', async () => {
|
||||
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||
|
||||
try {
|
||||
expect(await wsResponsePromise).toMatchObject({
|
||||
op: 'auth',
|
||||
req_id: 'contractUSDTPrivate-auth',
|
||||
success: true,
|
||||
wsKey: WS_KEY_MAP.contractUSDTPrivate,
|
||||
});
|
||||
} catch (e) {
|
||||
// sub failed
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
|
||||
// try {
|
||||
// expect(await wsUpdatePromise).toStrictEqual('');
|
||||
// } catch (e) {
|
||||
// // no data
|
||||
// expect(e).toBeFalsy();
|
||||
// }
|
||||
});
|
||||
});
|
||||
74
test/contract/ws.public.inverse.test.ts
Normal file
74
test/contract/ws.public.inverse.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
WebsocketClient,
|
||||
WSClientConfigurableOptions,
|
||||
WS_KEY_MAP,
|
||||
} from '../../src';
|
||||
import {
|
||||
logAllEvents,
|
||||
getSilentLogger,
|
||||
waitForSocketEvent,
|
||||
WS_OPEN_EVENT_PARTIAL,
|
||||
} from '../ws.util';
|
||||
|
||||
describe('Public Contract Inverse Websocket Client', () => {
|
||||
let wsClient: WebsocketClient;
|
||||
|
||||
const wsClientOptions: WSClientConfigurableOptions = {
|
||||
market: 'contractInverse',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
wsClient = new WebsocketClient(
|
||||
wsClientOptions,
|
||||
getSilentLogger('expectSuccessNoAuth')
|
||||
);
|
||||
wsClient.connectPublic();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||
try {
|
||||
expect(await wsOpenPromise).toMatchObject({
|
||||
event: WS_OPEN_EVENT_PARTIAL,
|
||||
wsKey: WS_KEY_MAP.contractInversePublic,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('open: ', e);
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should subscribe to public orderbook events', async () => {
|
||||
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||
|
||||
const wsTopic = 'orderbook.25.BTCUSD';
|
||||
wsClient.subscribe(wsTopic);
|
||||
|
||||
try {
|
||||
expect(await wsResponsePromise).toMatchObject({
|
||||
success: true,
|
||||
op: 'subscribe',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('response: ', e);
|
||||
// sub failed (or jest expect threw)
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
|
||||
try {
|
||||
expect(await wsUpdatePromise).toMatchObject({
|
||||
topic: wsTopic,
|
||||
data: {
|
||||
a: expect.any(Array),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
74
test/contract/ws.public.usdt.test.ts
Normal file
74
test/contract/ws.public.usdt.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
WebsocketClient,
|
||||
WSClientConfigurableOptions,
|
||||
WS_KEY_MAP,
|
||||
} from '../../src';
|
||||
import {
|
||||
logAllEvents,
|
||||
getSilentLogger,
|
||||
waitForSocketEvent,
|
||||
WS_OPEN_EVENT_PARTIAL,
|
||||
} from '../ws.util';
|
||||
|
||||
describe('Public Contract USDT Websocket Client', () => {
|
||||
let wsClient: WebsocketClient;
|
||||
|
||||
const wsClientOptions: WSClientConfigurableOptions = {
|
||||
market: 'contractUSDT',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
wsClient = new WebsocketClient(
|
||||
wsClientOptions,
|
||||
getSilentLogger('expectSuccessNoAuth')
|
||||
);
|
||||
wsClient.connectPublic();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||
try {
|
||||
expect(await wsOpenPromise).toMatchObject({
|
||||
event: WS_OPEN_EVENT_PARTIAL,
|
||||
wsKey: WS_KEY_MAP.contractUSDTPublic,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('open: ', e);
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should subscribe to public orderbook events', async () => {
|
||||
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||
|
||||
const wsTopic = 'orderbook.25.BTCUSDT';
|
||||
wsClient.subscribe(wsTopic);
|
||||
|
||||
try {
|
||||
expect(await wsResponsePromise).toMatchObject({
|
||||
success: true,
|
||||
op: 'subscribe',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('response: ', e);
|
||||
// sub failed (or jest expect threw)
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
|
||||
try {
|
||||
expect(await wsUpdatePromise).toMatchObject({
|
||||
topic: wsTopic,
|
||||
data: {
|
||||
a: expect.any(Array),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -110,6 +110,7 @@ describe('Private Inverse REST API POST Endpoints', () => {
|
||||
symbol,
|
||||
p_r_price: '50000',
|
||||
p_r_qty: 1,
|
||||
order_link_id: 'fakeorderid',
|
||||
})
|
||||
).toMatchObject({
|
||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('Private Inverse Perps Websocket Client', () => {
|
||||
// console.error()
|
||||
expect(e?.message).toStrictEqual('Unexpected server response: 401');
|
||||
}
|
||||
badClient.closeAll();
|
||||
badClient.closeAll(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('Private Inverse Perps Websocket Client', () => {
|
||||
|
||||
afterAll(() => {
|
||||
// await promiseSleep(2000);
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a ws connection', async () => {
|
||||
|
||||
@@ -24,7 +24,7 @@ describe('Public Inverse Perps Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -176,7 +176,9 @@ describe('Private Linear REST API POST Endpoints', () => {
|
||||
margin: 5,
|
||||
})
|
||||
).toMatchObject({
|
||||
// ret_msg: '',
|
||||
ret_code: API_ERROR_CODE.POSITION_SIZE_IS_ZERO,
|
||||
// ret_code: API_ERROR_CODE.ISOLATED_NOT_MODIFIED_LINEAR,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('Private Linear Perps Websocket Client', () => {
|
||||
} catch (e) {
|
||||
expect(e?.message).toStrictEqual('Unexpected server response: 401');
|
||||
}
|
||||
badClient.closeAll();
|
||||
badClient.closeAll(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('Private Linear Perps Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a ws connection', async () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('Public Linear Perps Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getSilentLogger,
|
||||
waitForSocketEvent,
|
||||
WS_OPEN_EVENT_PARTIAL,
|
||||
fullLogger,
|
||||
} from '../ws.util';
|
||||
|
||||
describe('Private Spot V1 Websocket Client', () => {
|
||||
@@ -30,13 +31,14 @@ describe('Private Spot V1 Websocket Client', () => {
|
||||
beforeAll(() => {
|
||||
wsClient = new WebsocketClient(
|
||||
wsClientOptions,
|
||||
// fullLogger
|
||||
getSilentLogger('expectSuccess')
|
||||
);
|
||||
logAllEvents(wsClient);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
// TODO: how to detect if auth failed for the v1 spot ws
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('Private Spot V3 Websocket Client', () => {
|
||||
} catch (e) {
|
||||
// console.error()
|
||||
}
|
||||
badClient.closeAll();
|
||||
badClient.closeAll(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('Private Spot V3 Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a private ws connection', async () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('Public Spot V1 Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('Public Spot V3 Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -12,10 +12,15 @@ import {
|
||||
} from '../ws.util';
|
||||
|
||||
describe('Private Unified Margin Websocket Client', () => {
|
||||
const API_KEY = process.env.API_KEY_COM;
|
||||
const API_SECRET = process.env.API_SECRET_COM;
|
||||
|
||||
let wsClient: WebsocketClient;
|
||||
|
||||
const wsClientOptions: WSClientConfigurableOptions = {
|
||||
market: 'unifiedPerp',
|
||||
key: API_KEY,
|
||||
secret: API_SECRET,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -29,7 +34,7 @@ describe('Private Unified Margin Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('Public Unified Margin Websocket Client (Options)', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -28,10 +28,6 @@ describe('Public Unified Margin Websocket Client (Perps - USDC)', () => {
|
||||
wsClient.connectPublic();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||
try {
|
||||
@@ -42,6 +38,8 @@ describe('Public Unified Margin Websocket Client (Perps - USDC)', () => {
|
||||
} catch (e) {
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
// TODO: are there USDC topics? This doesn't seem to work
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('Public Unified Margin Websocket Client (Perps - USDT)', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('Private USDC Option Websocket Client', () => {
|
||||
|
||||
// badClient.subscribe(wsTopic);
|
||||
badClient.removeAllListeners();
|
||||
badClient.closeAll();
|
||||
badClient.closeAll(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('Private USDC Option Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a private ws connection', async () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('Public USDC Option Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('Private USDC Perp Websocket Client', () => {
|
||||
|
||||
// badClient.subscribe(wsTopic);
|
||||
badClient.removeAllListeners();
|
||||
badClient.closeAll();
|
||||
badClient.closeAll(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('Private USDC Perp Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a private ws connection', async () => {
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('Public USDC Perp Websocket Client', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
wsClient.closeAll(true);
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
|
||||
Reference in New Issue
Block a user