From 9c12727b9d340497436d67a265bdaae41a8babbc Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Thu, 23 Jan 2025 16:53:12 +0000 Subject: [PATCH 01/23] chore(): add table of contents --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 7e23d64..598433c 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,40 @@ Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs and WebSockets: - See example for more details: [examples/ws-api-events.ts](./examples/ws-api-events.ts) - Active community support & collaboration in telegram: [Node.js Algo Traders](https://t.me/nodetraders). +# Table of Contents + +## Overview +- [Installation](#installation) +- [Issues & Discussion](#issues--discussion) +- [Related Projects](#related-projects) +- [Documentation](#documentation) + +## Structure & Usage +- [Structure](#structure) +- [API Clients](#api-clients) +- [Usage](#usage) + +## WebSocket Integration +- [WebSockets](#websockets) +- [WebSocket Subscriptions - Consuming Events](#websocket-subscriptions---consuming-events) +- [Websocket API - Sending Orders via WebSockets](#websocket-api---sending-orders-via-websockets) +- [Specifying Other Markets](#specifying-other-markets) +- [Load Balancing](#balancing-load-across-multiple-connections) +- [Older Websocket APIs](#older-websocket-apis) + +## Additional Features +- [Logging](#logging) + - [Customise Logging](#customise-logging) + - [Debug HTTP Requests](#debug-http-requests) +- [Browser Usage](#browser-usage) + - [Import](#import) + - [Webpack](#webpack) + +## Contributing +- [Contributions & Thanks](#contributions--thanks) + +------ + ## Installation `npm install --save bybit-api` From ee23e137101d6de939d406623b472a3bd800789f Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 12:44:01 +0000 Subject: [PATCH 02/23] chore(): ws client tidying and misc improvements --- src/util/BaseWSClient.ts | 9 +- src/util/websockets/websocket-util.ts | 41 +++ src/websocket-client.ts | 354 ++++++++------------------ test/ws.util.ts | 27 +- 4 files changed, 166 insertions(+), 265 deletions(-) diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index 7a3a618..43cd23e 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -908,7 +908,11 @@ export abstract class BaseWebsocketClient< /** * Try sending a string event on a WS connection (identified by the WS Key) */ - public tryWsSend(wsKey: TWSKey, wsMessage: string) { + public tryWsSend( + wsKey: TWSKey, + wsMessage: string, + throwExceptions?: boolean, + ) { try { this.logger.trace('Sending upstream ws message: ', { ...WS_LOGGER_CATEGORY, @@ -934,6 +938,9 @@ export abstract class BaseWebsocketClient< wsKey, exception: e, }); + if (throwExceptions) { + throw e; + } } } diff --git a/src/util/websockets/websocket-util.ts b/src/util/websockets/websocket-util.ts index 6317a1f..1454a31 100644 --- a/src/util/websockets/websocket-util.ts +++ b/src/util/websockets/websocket-util.ts @@ -4,6 +4,7 @@ import { CategoryV5, WebsocketClientOptions, WsKey, + WsTopic, } from '../../types'; import { DefaultLogger } from '../logger'; @@ -660,3 +661,43 @@ export function getNormalisedTopicRequests( } return normalisedTopicRequests; } + +/** + * Groups topics in request into per-wsKey groups + * @param normalisedTopicRequests + * @param wsKey + * @param isPrivateTopic + * @returns + */ +export function getTopicsPerWSKey( + normalisedTopicRequests: WsTopicRequest[], + wsKey?: WsKey, + isPrivateTopic?: boolean, +): { + [key in WsKey]?: WsTopicRequest[]; +} { + const perWsKeyTopics: { [key in WsKey]?: WsTopicRequest[] } = {}; + + // Sort into per wsKey arrays, in case topics are mixed together for different wsKeys + for (const topicRequest of normalisedTopicRequests) { + const derivedWsKey = + wsKey || + getWsKeyForTopic( + this.options.market, + topicRequest.topic, + isPrivateTopic, + topicRequest.category, + ); + + if ( + !perWsKeyTopics[derivedWsKey] || + !Array.isArray(perWsKeyTopics[derivedWsKey]) + ) { + perWsKeyTopics[derivedWsKey] = []; + } + + perWsKeyTopics[derivedWsKey]!.push(topicRequest); + } + + return perWsKeyTopics; +} diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 5e70b24..07d1288 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/no-explicit-any */ import WebSocket from 'isomorphic-ws'; import { @@ -19,6 +16,7 @@ import { getMaxTopicsPerSubscribeEvent, getNormalisedTopicRequests, getPromiseRefForWSAPIRequest, + getTopicsPerWSKey, getWsKeyForTopic, getWsUrl, isPrivateWsTopic, @@ -35,6 +33,7 @@ import { } from './util/BaseWSClient'; import { Exact, + WSAPIOperation, WSAPIRequest, WsAPIOperationResponseMap, WsAPITopicRequestParamMap, @@ -46,47 +45,6 @@ import { SignAlgorithm, signMessage } from './util/webCryptoAPI'; const WS_LOGGER_CATEGORY = { category: 'bybit-ws' }; -/** - * Groups topics in request into per-wsKey groups - * @param normalisedTopicRequests - * @param wsKey - * @param isPrivateTopic - * @returns - */ -function getTopicsPerWSKey( - normalisedTopicRequests: WsTopicRequest[], - wsKey?: WsKey, - isPrivateTopic?: boolean, -): { - [key in WsKey]?: WsTopicRequest[]; -} { - const perWsKeyTopics: { [key in WsKey]?: WsTopicRequest[] } = {}; - - // Sort into per wsKey arrays, in case topics are mixed together for different wsKeys - for (const topicRequest of normalisedTopicRequests) { - const derivedWsKey = - wsKey || - getWsKeyForTopic( - this.options.market, - topicRequest.topic, - isPrivateTopic, - topicRequest.category, - ); - - if ( - !perWsKeyTopics[derivedWsKey] || - !Array.isArray(perWsKeyTopics[derivedWsKey]) - ) { - perWsKeyTopics[derivedWsKey] = []; - } - - perWsKeyTopics[derivedWsKey]!.push(topicRequest); - } - - return perWsKeyTopics; -} - -// export class WebsocketClient extends EventEmitter { export class WebsocketClient extends BaseWebsocketClient< WsKey, WsRequestOperationBybit @@ -213,7 +171,6 @@ export class WebsocketClient extends BaseWebsocketClient< } /** - * * Subscribe to V5 topics & track/persist them. * @param wsTopics - topic or list of topics * @param category - the API category this topic is for (e.g. "linear"). The value is only important when connecting to public topics and will be ignored for private topics. @@ -338,6 +295,8 @@ export class WebsocketClient extends BaseWebsocketClient< } /** + * Note: subscribeV5() might be simpler to use. The end result is the same. + * * Request subscription to one or more topics. Pass topics as either an array of strings, or array of objects (if the topic has parameters). * Objects should be formatted as {topic: string, params: object, category: CategoryV5}. * @@ -368,6 +327,7 @@ export class WebsocketClient extends BaseWebsocketClient< } /** + * Note: unsubscribe() might be simpler to use. The end result is the same. * Unsubscribe from one or more topics. Similar to subscribe() but in reverse. * * - Requests are automatically routed to the correct websocket connection. @@ -393,108 +353,119 @@ export class WebsocketClient extends BaseWebsocketClient< } } - /******* - * - * - * - * - * OLD WS CLIENT BELOW + /** * * * + * WS API Methods - similar to the REST API, but via WebSockets + * https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline * * * */ /** - * Subscribe to V1-V3 topics & track/persist them. + * Send a Websocket API command/request on a connection. Returns a promise that resolves on reply. * - * @deprecated The V1-V3 websockets are very old and may not work properly anymore. Support for them will be removed soon. Use subcribeV5/unsubscribeV5 or subscribe/unsubscribe instead. + * WS API Documentation for list of operations and parameters: + * https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline * - * Note: for public V5 topics use the `subscribeV5()` method. + * Returned promise is rejected if: + * - an exception is detected in the reply, OR + * - the connection disconnects for any reason (even if automatic reconnect will happen). * - * Topics will be automatically resubscribed to if the connection resets/drops/reconnects. - * @param wsTopics - topic or list of topics - * @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) + * Authentication is automatic. If you didn't request authentication yourself, there might be a small delay after your first request, while the SDK automatically authenticates. + * + * @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported for Bybit, since that is the dedicated WS API connection. + * @param operation - The command being sent, e.g. "order.create" to submit a new order. + * @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new order. Only send parameters for the request body. Everything else is automatically handled. + * @returns Promise - tries to resolve with async WS API response. Rejects if disconnected or exception is seen in async WS API response */ - public subscribeV3( - wsTopics: WsTopic[] | WsTopic, - isPrivateTopic?: boolean, - ): Promise[] { - const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; - if (this.options.market === 'v5') { - topics.forEach((topic) => { - if (!isPrivateWsTopic(topic)) { - throw new Error( - 'For public "v5" websocket topics, use the subscribeV5() method & provide the category parameter', - ); - } - }); - } - const promises: Promise[] = []; + // This overload allows the caller to omit the 3rd param, if it isn't required + sendWSAPIRequest< + TWSKey extends keyof WsAPIWsKeyTopicMap, + TWSOperation extends WsAPIWsKeyTopicMap[TWSKey], + TWSParams extends Exact, + >( + wsKey: TWSKey, + operation: TWSOperation, + ...params: TWSParams extends undefined ? [] : [TWSParams] + ): Promise; - topics.forEach((topic) => { - const wsKey = getWsKeyForTopic( - this.options.market, - topic, - isPrivateTopic, + // These overloads give stricter types than mapped generics, since generic constraints do not trigger excess property checks + // Without these overloads, TypeScript won't complain if you include an unexpected property with your request (if it doesn't clash with an existing property) + sendWSAPIRequest( + wsKey: typeof WS_KEY_MAP.v5PrivateTrade, + operation: TWSOpreation, + params: WsAPITopicRequestParamMap[TWSOpreation], + ): Promise; + + sendWSAPIRequest( + wsKey: typeof WS_KEY_MAP.v5PrivateTrade, + operation: TWSOpreation, + params: WsAPITopicRequestParamMap[TWSOpreation], + ): Promise; + + sendWSAPIRequest( + wsKey: typeof WS_KEY_MAP.v5PrivateTrade, + operation: TWSOpreation, + params: WsAPITopicRequestParamMap[TWSOpreation], + ): Promise; + + async sendWSAPIRequest< + TWSKey extends keyof WsAPIWsKeyTopicMap, + TWSOperation extends WsAPIWsKeyTopicMap[TWSKey], + TWSParams extends Exact, + TWSAPIResponse extends + WsAPIOperationResponseMap[TWSOperation] = WsAPIOperationResponseMap[TWSOperation], + >( + wsKey: WsKey = WS_KEY_MAP.v5PrivateTrade, + operation: TWSOperation, + params: TWSParams, + ): Promise { + this.logger.trace(`sendWSAPIRequest(): assert "${wsKey}" is connected`); + await this.assertIsConnected(wsKey); + this.logger.trace('sendWSAPIRequest()->assertIsConnected() ok'); + + await this.assertIsAuthenticated(wsKey); + this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok'); + + const requestEvent: WSAPIRequest = { + reqId: this.getNewRequestId(), + header: { + 'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`, + 'X-BAPI-TIMESTAMP': `${Date.now()}`, + Referer: APIID, + }, + op: operation, + args: [params], + }; + + // Sign, if needed + const signedEvent = await this.signWSAPIRequest(requestEvent); + + // Store deferred promise, resolved within the "resolveEmittableEvents" method while parsing incoming events + const promiseRef = getPromiseRefForWSAPIRequest(requestEvent); + + const deferredPromise = + this.getWsStore().createDeferredPromise( + wsKey, + promiseRef, + false, ); - const wsRequest: WsTopicRequest = { - topic: topic, - }; + this.logger.trace( + `sendWSAPIRequest(): sending raw request: ${JSON.stringify(signedEvent, null, 2)}`, + ); - // Persist topic for reconnects - const requestPromise = this.subscribeTopicsForWsKey([wsRequest], wsKey); + // Send event + this.tryWsSend(wsKey, JSON.stringify(signedEvent)); - promises.push(requestPromise); - }); + this.logger.trace(`sendWSAPIRequest(): sent ${operation} event`); - // Return promise to resolve midflight WS request (only works if already connected before request) - return promises; - } - - /** - * Unsubscribe from V1-V3 topics & remove them from memory. They won't be re-subscribed to if the connection reconnects. - * - * @deprecated The V1-V3 websockets are very old and may not work properly anymore. Support for them will be removed soon. Use subcribeV5/unsubscribeV5 or subscribe/unsubscribe instead. - * - * Note: For public V5 topics, use `unsubscribeV5()` instead! - * - * @param wsTopics topic or list of topics - * @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) - */ - public unsubscribeV3( - wsTopics: WsTopic[] | WsTopic, - isPrivateTopic?: boolean, - ) { - const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; - if (this.options.market === 'v5') { - topics.forEach((topic) => { - if (!isPrivateWsTopic(topic)) { - throw new Error( - 'For public "v5" websocket topics, use the unsubscribeV5() method & provide the category parameter', - ); - } - }); - } - - topics.forEach((topic) => { - const wsKey = getWsKeyForTopic( - this.options.market, - topic, - isPrivateTopic, - ); - - const wsRequest: WsTopicRequest = { - topic: topic, - }; - - // Persist topic for reconnects - this.unsubscribeTopicsForWsKey([wsRequest], wsKey); - }); + // Return deferred promise, so caller can await this call + return deferredPromise.promise!; } /** @@ -540,7 +511,9 @@ export class WebsocketClient extends BaseWebsocketClient< return await signMessage(paramsStr, secret, method, algorithm); } - protected async getWsAuthRequestEvent(wsKey: WsKey): Promise { + protected async getWsAuthRequestEvent( + wsKey: WsKey, + ): Promise> { try { const { signature, expiresAt } = await this.getWsAuthSignature(wsKey); @@ -656,6 +629,7 @@ export class WebsocketClient extends BaseWebsocketClient< if (wsRequestBuildingErrors.length) { const label = wsRequestBuildingErrors.length === requests.length ? 'all' : 'some'; + this.logger.error( `Failed to build/send ${wsRequestBuildingErrors.length} event(s) for ${label} WS requests due to exceptions`, { @@ -707,6 +681,7 @@ export class WebsocketClient extends BaseWebsocketClient< return isPrivateWsTopic(topicName); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any protected isWsPing(msg: any): boolean { if (!msg) { return false; @@ -726,6 +701,7 @@ export class WebsocketClient extends BaseWebsocketClient< return false; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any protected isWsPong(msg: any): boolean { if (!msg) { return false; @@ -763,7 +739,6 @@ export class WebsocketClient extends BaseWebsocketClient< event: MessageEventLike, ): EmittableEvent[] { const results: EmittableEvent[] = []; - // const isWSAPIResponseEvent = wsKey === WS_KEY_MAP.v5PrivateTrade; try { const parsed = JSON.parse(event.data); @@ -879,7 +854,6 @@ export class WebsocketClient extends BaseWebsocketClient< results.push({ eventType: 'update', event: parsed, - // isWSAPIResponse: isWSAPIResponseEvent, }); return results; } @@ -891,7 +865,6 @@ export class WebsocketClient extends BaseWebsocketClient< results.push({ eventType: 'error', event: parsed, - // isWSAPIResponse: isWSAPIResponseEvent, }); return results; } @@ -901,7 +874,6 @@ export class WebsocketClient extends BaseWebsocketClient< results.push({ eventType: 'response', event: parsed, - // isWSAPIResponse: isWSAPIResponseEvent, }); return results; } @@ -911,7 +883,6 @@ export class WebsocketClient extends BaseWebsocketClient< results.push({ eventType: 'authenticated', event: parsed, - // isWSAPIResponse: isWSAPIResponseEvent, }); return results; } @@ -950,121 +921,4 @@ export class WebsocketClient extends BaseWebsocketClient< return results; } - - /** - * - * - * - * WS API Methods - similar to the REST API, but via WebSockets - * - * - * - */ - - /** - * Send a Websocket API event on a connection. Returns a promise that resolves on reply. - * - * Authentication is automatic. If you didn't request authentication yourself, there might be a small delay after your first request, while the SDK automatically authenticates. - * - * Returned promise is rejected if: - * - an exception is detected in the reply, OR - * - the connection disconnects for any reason (even if automatic reconnect will happen). - * - * If you authenticated once and you're reconnected later (e.g. connection temporarily lost), the SDK will by default automatically: - * - Detect you were authenticated to the WS API before - * - Try to re-authenticate (up to 5 times, in case something (bad timestamp) goes wrong) - * - If it succeeds, it will emit the 'authenticated' event. - * - If it fails and gives up, it will emit an 'exception' event. - * - * @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported, since that is the dedicated WS API connection. - * @param operation - The command being sent, e.g. "order.create" to submit a new order. - * @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new order. Only send parameters for the request body. Everything else is automatically handled. - * @returns Promise - tries to resolve with async WS API response. Rejects if disconnected or exception is seen in async WS API response - */ - - // This overload allows the caller to omit the 3rd param, if it isn't required - sendWSAPIRequest< - TWSKey extends keyof WsAPIWsKeyTopicMap, - TWSOperation extends WsAPIWsKeyTopicMap[TWSKey], - TWSParams extends Exact, - >( - wsKey: TWSKey, - operation: TWSOperation, - ...params: TWSParams extends undefined ? [] : [TWSParams] - ): Promise; - - // These overloads give stricter types than mapped generics, since generic constraints do not trigger excess property checks - // Without these overloads, TypeScript won't complain if you include an unexpected property with your request (if it doesn't clash with an existing property) - sendWSAPIRequest( - wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: 'order.create', - params: WsAPITopicRequestParamMap['order.create'], - ): Promise; - - sendWSAPIRequest( - wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: 'order.amend', - params: WsAPITopicRequestParamMap['order.amend'], - ): Promise; - - sendWSAPIRequest( - wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: 'order.cancel', - params: WsAPITopicRequestParamMap['order.cancel'], - ): Promise; - - async sendWSAPIRequest< - TWSKey extends keyof WsAPIWsKeyTopicMap, - TWSOperation extends WsAPIWsKeyTopicMap[TWSKey], - TWSParams extends Exact, - TWSAPIResponse extends - WsAPIOperationResponseMap[TWSOperation] = WsAPIOperationResponseMap[TWSOperation], - >( - wsKey: WsKey = WS_KEY_MAP.v5PrivateTrade, - operation: TWSOperation, - params: TWSParams, - ): Promise { - this.logger.trace(`sendWSAPIRequest(): assert "${wsKey}" is connected`); - await this.assertIsConnected(wsKey); - this.logger.trace('sendWSAPIRequest()->assertIsConnected() ok'); - - await this.assertIsAuthenticated(wsKey); - this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok'); - - const requestEvent: WSAPIRequest = { - reqId: this.getNewRequestId(), - header: { - 'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`, - 'X-BAPI-TIMESTAMP': `${Date.now()}`, - Referer: APIID, - }, - op: operation, - args: [params], - }; - - // Sign, if needed - const signedEvent = await this.signWSAPIRequest(requestEvent); - - // Store deferred promise, resolved within the "resolveEmittableEvents" method while parsing incoming events - const promiseRef = getPromiseRefForWSAPIRequest(requestEvent); - - const deferredPromise = - this.getWsStore().createDeferredPromise( - wsKey, - promiseRef, - false, - ); - - this.logger.trace( - `sendWSAPIRequest(): sending raw request: ${JSON.stringify(signedEvent, null, 2)}`, - ); - - // Send event - this.tryWsSend(wsKey, JSON.stringify(signedEvent)); - - this.logger.trace(`sendWSAPIRequest(): sent ${operation} event`); - - // Return deferred promise, so caller can await this call - return deferredPromise.promise!; - } } diff --git a/test/ws.util.ts b/test/ws.util.ts index dd800d5..a8c1534 100644 --- a/test/ws.util.ts +++ b/test/ws.util.ts @@ -1,31 +1,30 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-unused-vars */ /* eslint-disable @typescript-eslint/no-empty-function */ -import { WebsocketClient, WsClientEvent } from '../src'; +import { DefaultLogger, WebsocketClient } from '../src'; -export function getSilentLogger(_logHint?: string) { +export function getSilentLogger(_logHint?: string): typeof DefaultLogger { return { - silly: () => {}, - debug: () => {}, - notice: () => {}, + trace: () => {}, info: () => {}, - warning: () => {}, error: () => {}, }; } -export const fullLogger = { - silly: (...params) => console.log('silly', ...params), - debug: (...params) => console.log('debug', ...params), - notice: (...params) => console.log('notice', ...params), +export const fullLogger: typeof DefaultLogger = { + trace: (...params) => console.log('trace', ...params), info: (...params) => console.info('info', ...params), - warning: (...params) => console.warn('warning', ...params), error: (...params) => console.error('error', ...params), }; -export const WS_OPEN_EVENT_PARTIAL = { - type: 'open', -}; +export type WsClientEvent = + | 'open' + | 'update' + | 'close' + | 'error' + | 'reconnect' + | 'reconnected' + | 'response'; /** Resolves a promise if an event is seen before a timeout (defaults to 4.5 seconds) */ export function waitForSocketEvent( From e4957b049975655dcecdd6939e71a34ade6ec981 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 15:02:02 +0000 Subject: [PATCH 03/23] chore(): remove decommissioned REST and WS clients --- examples/deprecated/rest-contract-private.ts | 24 -- examples/deprecated/rest-contract-public.js | 21 - examples/deprecated/rest-contract-public.ts | 20 - examples/deprecated/rest-copy-private.ts | 25 -- examples/deprecated/rest-linear-public.ts | 23 - examples/deprecated/rest-raw-v3sign.ts | 33 -- examples/deprecated/rest-spot-public.ts | 18 - examples/deprecated/rest-spot-tpsl.ts | 42 -- .../rest-unified-margin-private-cursor.ts | 44 -- .../rest-unified-margin-public-cursor.ts | 36 -- .../deprecated/ws-private-copytrading-v3.ts | 61 --- examples/deprecated/ws-private.ts | 69 --- examples/deprecated/ws-public.ts | 172 -------- src/account-asset-client-v3.ts | 314 -------------- src/account-asset-client.ts | 153 ------- src/contract-client.ts | 364 ---------------- src/copy-trading-client.ts | 162 ------- src/index.ts | 11 - src/inverse-client.ts | 341 --------------- src/inverse-futures-client.ts | 407 ------------------ src/linear-client.ts | 391 ----------------- src/spot-client-v3.ts | 265 +----------- src/spot-client.ts | 183 -------- src/types/request/index.ts | 1 - src/types/request/spot.ts | 89 ---- src/types/shared.ts | 32 +- src/types/websockets/ws-general.ts | 16 +- src/unified-margin-client.ts | 401 ----------------- src/usdc-option-client.ts | 337 --------------- src/usdc-perpetual-client.ts | 320 -------------- src/util/websockets/websocket-util.ts | 315 +------------- src/websocket-client.ts | 149 ++----- 32 files changed, 56 insertions(+), 4783 deletions(-) delete mode 100644 examples/deprecated/rest-contract-private.ts delete mode 100644 examples/deprecated/rest-contract-public.js delete mode 100644 examples/deprecated/rest-contract-public.ts delete mode 100644 examples/deprecated/rest-copy-private.ts delete mode 100644 examples/deprecated/rest-linear-public.ts delete mode 100644 examples/deprecated/rest-raw-v3sign.ts delete mode 100644 examples/deprecated/rest-spot-public.ts delete mode 100644 examples/deprecated/rest-spot-tpsl.ts delete mode 100644 examples/deprecated/rest-unified-margin-private-cursor.ts delete mode 100644 examples/deprecated/rest-unified-margin-public-cursor.ts delete mode 100644 examples/deprecated/ws-private-copytrading-v3.ts delete mode 100644 examples/deprecated/ws-private.ts delete mode 100644 examples/deprecated/ws-public.ts delete mode 100644 src/account-asset-client-v3.ts delete mode 100644 src/account-asset-client.ts delete mode 100644 src/contract-client.ts delete mode 100644 src/copy-trading-client.ts delete mode 100644 src/inverse-client.ts delete mode 100644 src/inverse-futures-client.ts delete mode 100644 src/linear-client.ts delete mode 100644 src/spot-client.ts delete mode 100644 src/types/request/spot.ts delete mode 100644 src/unified-margin-client.ts delete mode 100644 src/usdc-option-client.ts delete mode 100644 src/usdc-perpetual-client.ts diff --git a/examples/deprecated/rest-contract-private.ts b/examples/deprecated/rest-contract-private.ts deleted file mode 100644 index 59e1484..0000000 --- a/examples/deprecated/rest-contract-private.ts +++ /dev/null @@ -1,24 +0,0 @@ -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); - } -})(); diff --git a/examples/deprecated/rest-contract-public.js b/examples/deprecated/rest-contract-public.js deleted file mode 100644 index d973b47..0000000 --- a/examples/deprecated/rest-contract-public.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * This is the pure javascript version of the `rest-contract-public.ts` sample - */ - -// To use a local build (testing with the repo directly), make sure to `npm run build` first from the repo root -// const { ContractClient } = require('../dist'); - -// or, use the version installed with npm -const { ContractClient } = require('bybit-api'); - -(async () => { - const client = new ContractClient(); - - try { - const orderbookResult = await client.getOrderBook('BTCUSDT', 'linear'); - console.log('orderbook result: ', orderbookResult); - } catch (e) { - console.error('request failed: ', e); - } - -})(); diff --git a/examples/deprecated/rest-contract-public.ts b/examples/deprecated/rest-contract-public.ts deleted file mode 100644 index 1fecb0b..0000000 --- a/examples/deprecated/rest-contract-public.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * This is the TypeScript version of the `rest-contract-public.js` sample. - */ - -// For testing with the repo directly, import from the src folder -import { ContractClient } from '../../src'; - -// or, use the version installed with npm -// import { ContractClient } from 'bybit-api'; - -(async () => { - const client = new ContractClient(); - - try { - const orderbookResult = await client.getOrderBook('BTCUSDT', 'linear'); - console.log('orderbook result: ', orderbookResult); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-copy-private.ts b/examples/deprecated/rest-copy-private.ts deleted file mode 100644 index ebced30..0000000 --- a/examples/deprecated/rest-copy-private.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CopyTradingClient } from '../../src/index'; - -// or -// import { CopyTradingClient } from 'bybit-api'; - -const key = process.env.API_KEY_COM; -const secret = process.env.API_SECRET_COM; - -const client = new CopyTradingClient({ - key, - secret, - strict_param_validation: true, -}); - -(async () => { - try { - const res = await client.closeOrder({ - symbol: 'BTCUSDT', - parentOrderId: '419190fe-016c-469a-810e-936bef2f1234', - }); - console.log('res:', res); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-linear-public.ts b/examples/deprecated/rest-linear-public.ts deleted file mode 100644 index d241ee6..0000000 --- a/examples/deprecated/rest-linear-public.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { LinearClient } from '../../src/index'; - -// or -// import { LinearClient } from 'bybit-api'; - -const client = new LinearClient(); - -(async () => { - try { - // console.log('getSymbols: ', await client.getSymbols()); - // console.log('getOrderBook: ', await client.getOrderBook(symbol)); - console.log( - 'getKline: ', - await client.getKline({ - symbol: 'ETHUSDT', - interval: 'D', - from: 1, - }), - ); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-raw-v3sign.ts b/examples/deprecated/rest-raw-v3sign.ts deleted file mode 100644 index 35308a3..0000000 --- a/examples/deprecated/rest-raw-v3sign.ts +++ /dev/null @@ -1,33 +0,0 @@ -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 { - /** - * You can make raw HTTP requests without the per-endpoint abstraction. - * - * The REST ContractClient uses bybit's v3 signature mechanism, - * so it can be used for raw calls to any v3-supporting endpoints (incl the V5 APIs). - * e.g. if an endpoint is missing and you desperately need it (but please raise an issue or PR if you're missing an endpoint) - */ - const rawCall = await client.getPrivate('/v5/order/realtime', { - category: 'linear', - symbol: 'BTCUSDT', - }); - - console.log('rawCall:', rawCall); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-spot-public.ts b/examples/deprecated/rest-spot-public.ts deleted file mode 100644 index 0574365..0000000 --- a/examples/deprecated/rest-spot-public.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SpotClientV3 } from '../../src/index'; - -// or -// import { SpotClientV3 } from 'bybit-api'; - -const client = new SpotClientV3(); - -const symbol = 'BTCUSDT'; - -(async () => { - try { - // console.log('getSymbols: ', await client.getSymbols()); - // console.log('getOrderBook: ', await client.getOrderBook(symbol)); - console.log('getOrderBook: ', await client.getOrderBook(symbol)); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-spot-tpsl.ts b/examples/deprecated/rest-spot-tpsl.ts deleted file mode 100644 index d296d6b..0000000 --- a/examples/deprecated/rest-spot-tpsl.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { SpotClientV3 } from '../../src/index'; - -// or -// import { SpotClientV3 } from 'bybit-api'; - -const symbol = 'BTCUSDT'; -const key = process.env.API_KEY_COM; -const secret = process.env.API_SECRET_COM; - -const client = new SpotClientV3({ - key, - secret, - strict_param_validation: true, -}); - -(async () => { - try { - const orderId = undefined; - const ordersPerPage = undefined; - - const orders = await client.getOpenOrders(symbol); - console.log('orders 1:', orders); - - const normalOrders = await client.getOpenOrders( - symbol, - orderId, - ordersPerPage, - 0, - ); - console.log('normal orders:', normalOrders); - - const tpSlOrders = await client.getOpenOrders( - symbol, - orderId, - ordersPerPage, - 1, - ); - console.log('tpSlOrders:', tpSlOrders); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-unified-margin-private-cursor.ts b/examples/deprecated/rest-unified-margin-private-cursor.ts deleted file mode 100644 index 0bde7cb..0000000 --- a/examples/deprecated/rest-unified-margin-private-cursor.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UnifiedMarginClient } from '../../src/index'; - -// or -// import { UnifiedMarginClient } from 'bybit-api'; - -const key = process.env.API_KEY_COM; -const secret = process.env.API_SECRET_COM; - -const client = new UnifiedMarginClient({ - key, - secret, - strict_param_validation: true, -}); - -(async () => { - try { - // page 1 - const historicOrders1 = await client.getHistoricOrders({ - category: 'linear', - limit: 1, - // cursor, - }); - console.log('page 1:', JSON.stringify(historicOrders1, null, 2)); - - // page 2 - const historicOrders2 = await client.getHistoricOrders({ - category: 'linear', - limit: 1, - cursor: historicOrders1.result.nextPageCursor, - }); - console.log('page 2:', JSON.stringify(historicOrders2, null, 2)); - - const historicOrdersBoth = await client.getHistoricOrders({ - category: 'linear', - limit: 2, - }); - console.log( - 'both to compare:', - JSON.stringify(historicOrdersBoth, null, 2), - ); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/rest-unified-margin-public-cursor.ts b/examples/deprecated/rest-unified-margin-public-cursor.ts deleted file mode 100644 index 224ed1a..0000000 --- a/examples/deprecated/rest-unified-margin-public-cursor.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { UnifiedMarginClient } from '../../src/index'; - -// or -// import { UnifiedMarginClient } from 'bybit-api'; - -const client = new UnifiedMarginClient({ - strict_param_validation: true, -}); - -(async () => { - try { - // page 1 - const historicOrders1 = await client.getInstrumentInfo({ - category: 'linear', - limit: '2', - }); - console.log('page 1:', JSON.stringify(historicOrders1, null, 2)); - - // page 2 - const historicOrders2 = await client.getInstrumentInfo({ - category: 'linear', - limit: '2', - cursor: historicOrders1.result.nextPageCursor, - }); - console.log('page 2:', JSON.stringify(historicOrders2, null, 2)); - - // page 1 & 2 in one request (for comparison) - const historicOrdersBoth = await client.getInstrumentInfo({ - category: 'linear', - limit: '4', - }); - console.log('both pages', JSON.stringify(historicOrdersBoth, null, 2)); - } catch (e) { - console.error('request failed: ', e); - } -})(); diff --git a/examples/deprecated/ws-private-copytrading-v3.ts b/examples/deprecated/ws-private-copytrading-v3.ts deleted file mode 100644 index 17e445d..0000000 --- a/examples/deprecated/ws-private-copytrading-v3.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable no-unused-vars */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-empty-function */ -import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../../src'; - -// or -// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api'; - -const logger = { - ...DefaultLogger, - silly: (...params) => { - // console.log(params); - }, -}; - -const key = process.env.API_KEY; -const secret = process.env.API_SECRET; - -/** - * Copy trading api docs say that private topics should connect to: wss://stream.bybit.com/realtime_private - * - * Within this SDK, only the market `linear` uses this endpoint for private topics: - */ -const market = 'linear'; - -const wsClient = new WebsocketClient( - { - key: key, - secret: secret, - market: market, - }, - logger, -); - -wsClient.on('update', (data) => { - console.log('raw message received ', JSON.stringify(data, null, 2)); -}); - -wsClient.on('open', (data) => { - console.log('connection opened open:', data.wsKey); -}); -wsClient.on('response', (data) => { - console.log('ws response: ', JSON.stringify(data, null, 2)); -}); -wsClient.on('reconnect', ({ wsKey }) => { - console.log('ws automatically reconnecting.... ', wsKey); -}); -wsClient.on('reconnected', (data) => { - console.log('ws has reconnected ', data?.wsKey); -}); -wsClient.on('error', (data) => { - console.error('ws exception: ', data); -}); - -// copy trading topics from api docs: https://bybit-exchange.github.io/docs/copy-trade/ws-private/position -wsClient.subscribe([ - 'copyTradePosition', - 'copyTradeOrder', - 'copyTradeExecution', - 'copyTradeWallet', -]); diff --git a/examples/deprecated/ws-private.ts b/examples/deprecated/ws-private.ts deleted file mode 100644 index 2ee6faf..0000000 --- a/examples/deprecated/ws-private.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable no-unused-vars */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-empty-function */ -import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../../src'; - -// or -// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api'; - -const logger = { - ...DefaultLogger, - silly: () => {}, -}; - -const key = process.env.API_KEY; -const secret = process.env.API_SECRET; - -// USDT Perps: -// const market = 'linear'; -// Inverse Perp -// const market = 'inverse'; -// const market = 'spotv3'; -// Contract v3 -const market = 'contractUSDT'; -// const market = 'contractInverse'; - -const wsClient = new WebsocketClient( - { - key: key, - secret: secret, - market: market, - // testnet: true, - restOptions: { - // enable_time_sync: true, - }, - }, - logger, -); - -wsClient.on('update', (data) => { - console.log('raw message received ', JSON.stringify(data, null, 2)); -}); - -wsClient.on('open', (data) => { - console.log('connection opened open:', data.wsKey); -}); -wsClient.on('response', (data) => { - console.log('ws response: ', JSON.stringify(data, null, 2)); -}); -wsClient.on('reconnect', ({ wsKey }) => { - console.log('ws automatically reconnecting.... ', wsKey); -}); -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']); - -// Contract v3 -wsClient.subscribe([ - 'user.position.contractAccount', - 'user.execution.contractAccount', - 'user.order.contractAccount', - 'user.wallet.contractAccount', -]); diff --git a/examples/deprecated/ws-public.ts b/examples/deprecated/ws-public.ts deleted file mode 100644 index e1cdb0a..0000000 --- a/examples/deprecated/ws-public.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../../src'; - -// or -// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api'; - -const logger = { - ...DefaultLogger, - silly: (...params) => console.log('silly', ...params), -}; - -const wsClient = new WebsocketClient( - { - // key: key, - // secret: secret, - // market: 'linear', - // market: 'inverse', - // market: 'spot', - // market: 'spotv3', - // market: 'usdcOption', - // market: 'usdcPerp', - // market: 'unifiedPerp', - // market: 'unifiedOption', - market: 'contractUSDT', - }, - logger, -); - -wsClient.on('update', (data) => { - console.log('raw message received ', JSON.stringify(data)); - // console.log('raw message received ', JSON.stringify(data, null, 2)); -}); - -wsClient.on('open', (data) => { - console.log('connection opened open:', data.wsKey); - - // if (data.wsKey === WS_KEY_MAP.spotPublic) { - // // Spot public, but not recommended - use spotv3 client instead - // // The old spot websockets dont automatically resubscribe if they disconnect - // // wsClient.subscribePublicSpotTrades('BTCUSDT'); - // // wsClient.subscribePublicSpotTradingPair('BTCUSDT'); - // // wsClient.subscribePublicSpotV1Kline('BTCUSDT', '1m'); - // // wsClient.subscribePublicSpotOrderbook('BTCUSDT', 'full'); - // } -}); -wsClient.on('response', (data) => { - console.log('log response: ', JSON.stringify(data, null, 2)); -}); -wsClient.on('reconnect', ({ wsKey }) => { - console.log('ws automatically reconnecting.... ', wsKey); -}); -wsClient.on('reconnected', (data) => { - console.log('ws has reconnected ', data?.wsKey); -}); -// wsClient.on('error', (data) => { -// console.error('ws exception: ', data); -// }); - -// Inverse -// wsClient.subscribe('trade'); - -// Linear -// wsClient.subscribe('trade.BTCUSDT'); - -// Spot V3 -// wsClient.subscribe('trade.BTCUSDT'); -// Or an array of topics -// wsClient.subscribe([ -// 'orderbook.40.BTCUSDT', -// 'orderbook.40.BTCUSDC', -// 'orderbook.40.USDCUSDT', -// 'orderbook.40.BTCDAI', -// 'orderbook.40.DAIUSDT', -// 'orderbook.40.ETHUSDT', -// 'orderbook.40.ETHUSDC', -// 'orderbook.40.ETHDAI', -// 'orderbook.40.XRPUSDT', -// 'orderbook.40.XRPUSDC', -// 'orderbook.40.EOSUSDT', -// 'orderbook.40.EOSUSDC', -// 'orderbook.40.DOTUSDT', -// 'orderbook.40.DOTUSDC', -// 'orderbook.40.XLMUSDT', -// 'orderbook.40.XLMUSDC', -// 'orderbook.40.LTCUSDT', -// 'orderbook.40.LTCUSDC', -// 'orderbook.40.DOGEUSDT', -// 'orderbook.40.DOGEUSDC', -// 'orderbook.40.BITUSDT', -// 'orderbook.40.BITUSDC', -// 'orderbook.40.BITDAI', -// 'orderbook.40.CHZUSDT', -// 'orderbook.40.CHZUSDC', -// 'orderbook.40.MANAUSDT', -// 'orderbook.40.MANAUSDC', -// 'orderbook.40.LINKUSDT', -// 'orderbook.40.LINKUSDC', -// 'orderbook.40.ICPUSDT', -// 'orderbook.40.ICPUSDC', -// 'orderbook.40.ADAUSDT', -// 'orderbook.40.ADAUSDC', -// 'orderbook.40.SOLUSDC', -// 'orderbook.40.SOLUSDT', -// 'orderbook.40.MATICUSDC', -// 'orderbook.40.MATICUSDT', -// 'orderbook.40.SANDUSDC', -// 'orderbook.40.SANDUSDT', -// 'orderbook.40.LUNCUSDC', -// 'orderbook.40.LUNCUSDT', -// 'orderbook.40.SLGUSDC', -// 'orderbook.40.SLGUSDT', -// 'orderbook.40.AVAXUSDC', -// 'orderbook.40.AVAXUSDT', -// 'orderbook.40.OPUSDC', -// 'orderbook.40.OPUSDT', -// 'orderbook.40.OKSEUSDC', -// 'orderbook.40.OKSEUSDT', -// 'orderbook.40.APEXUSDC', -// 'orderbook.40.APEXUSDT', -// 'orderbook.40.TRXUSDC', -// 'orderbook.40.TRXUSDT', -// 'orderbook.40.GMTUSDC', -// 'orderbook.40.GMTUSDT', -// 'orderbook.40.SHIBUSDC', -// 'orderbook.40.SHIBUSDT', -// 'orderbook.40.LDOUSDC', -// 'orderbook.40.LDOUSDT', -// 'orderbook.40.APEUSDC', -// 'orderbook.40.APEUSDT', -// 'orderbook.40.FILUSDC', -// 'orderbook.40.FILUSDT', -// ]); - -// usdc options -// wsClient.subscribe([ -// `recenttrades.BTC`, -// `recenttrades.ETH`, -// `recenttrades.SOL`, -// ]); - -// usdc perps (note: the syntax is different for the unified perp market) -// (market: 'usdcPerp') -// wsClient.subscribe('trade.BTCUSDC'); -// wsClient.subscribe('instrument_info.100ms.BTCPERP'); - -// unified perps -// wsClient.subscribe('publicTrade.BTCUSDT'); -// wsClient.subscribe('publicTrade.BTCPERP'); - -// For spot v1 (the old, deprecated client), request public connection first then send required topics on 'open' -// Not necessary for spot v3 -// wsClient.connectPublic(); - -// To unsubscribe from topics (after a 5 second delay, in this example): -// setTimeout(() => { -// console.log('unsubscribing'); -// wsClient.unsubscribe('trade.BTCUSDT'); -// }, 5 * 1000); - -// Topics are tracked per websocket type -// Get a list of subscribed topics (e.g. for public v3 spot topics) (after a 5 second delay) -setTimeout(() => { - const publicSpotTopics = wsClient - .getWsStore() - .getTopics(WS_KEY_MAP.spotV3Public); - - console.log('public spot topics: ', publicSpotTopics); - - const privateSpotTopics = wsClient - .getWsStore() - .getTopics(WS_KEY_MAP.spotV3Private); - console.log('private spot topics: ', privateSpotTopics); -}, 5 * 1000); diff --git a/src/account-asset-client-v3.ts b/src/account-asset-client-v3.ts deleted file mode 100644 index 9b1adb1..0000000 --- a/src/account-asset-client-v3.ts +++ /dev/null @@ -1,314 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIKeyInfoV3, - APIResponseV3WithTime, - APIResponseWithTime, - AccountCoinBalanceResponseV3, - AccountCoinBalancesRequestV3, - AccountCoinBalancesResponseV3, - AssetInfoRequestV3, - AssetInfoResponseV3, - CoinInfoQueryResponseV3, - CreateSubAPIKeyRequestV3, - CreateSubAPIKeyResponseV3, - CreateSubMemberRequestV3, - CreateSubMemberResponseV3, - DepositAddressResponseV3, - DepositRecordQueryRequestV3, - DepositRecordQueryResponseV3, - InternalTransferRequestV3, - ModifyAPIKeyRequestV3, - QueryDepositAddressRequestV3, - QueryInternalTransferSResponseV3, - QueryInternalTransfersRequestV3, - QuerySubAccountDepositAddressRequestV3, - SingleAccountCoinBalanceRequestV3, - SubAccountTransferRequestV3, - SubAccountTransferResponseV3, - SubDepositRecordQueryRequestV3, - SubMemberResponseV3, - SupportedDepositListRequestV3, - SupportedDepositListResponseV3, - TransferCoinListRequestV3, - UniversalTransferCreateResponse, - UniversalTransferListRequestV3, - UniversalTransferListResponseV3, - UniversalTransferRequestV3, - WithdrawCreateRequestV3, - WithdrawRecordQueryRequestV3, - WithdrawRecordsQueryResponseV3, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Account Asset V3 APIs - * @deprecated WARNING - * These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024. - * They may stop working at any point before then. - * Please update your code as soon as possible to use the V5 APIs instead. - */ -export class AccountAssetClientV3 extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.v3; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time); - } - - getServerTime(): Promise< - APIResponseV3WithTime<{ timeSecond: string; timeNano: string }> - > { - return this.get('/v3/public/time'); - } - - /** - * - * Transfer Data Endpoints - * - */ - - createInternalTransfer( - params: InternalTransferRequestV3, - ): Promise> { - return this.postPrivate( - '/asset/v3/private/transfer/inter-transfer', - params, - ); - } - - getInternalTransfers( - params: QueryInternalTransfersRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/inter-transfer/list/query', - params, - ); - } - - createSubAccountTransfer(params: { - transferId: string; - coin: string; - amount: string; - subMemberId: number; - type: 'IN' | 'OUT'; - }): Promise> { - return this.postPrivate( - '/asset/v3/private/transfer/sub-member-transfer', - params, - ); - } - - getSubAccountTransfers( - params?: SubAccountTransferRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/sub-member-transfer/list/query', - params, - ); - } - - getSubAccounts(): Promise< - APIResponseWithTime<{ - subMemberIds: string[]; - transferableSubMemberIds: string[]; - }> - > { - return this.getPrivate('/asset/v3/private/transfer/sub-member/list/query'); - } - - enableUniversalTransfer(params?: { - subMemberIds?: string; - }): Promise> { - return this.postPrivate( - '/asset/v3/private/transfer/transfer-sub-member-save', - params, - ); - } - - createUniversalTransfer( - params: UniversalTransferRequestV3, - ): Promise> { - return this.postPrivate( - '/asset/v3/private/transfer/universal-transfer', - params, - ); - } - - getUniversalTransfers( - params: UniversalTransferListRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/universal-transfer/list/query', - params, - ); - } - - getTransferableCoinList( - params: TransferCoinListRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/transfer-coin/list/query', - params, - ); - } - - getAccountCoinBalance( - params: SingleAccountCoinBalanceRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/account-coin/balance/query', - params, - ); - } - - getAccountCoinBalances( - params: AccountCoinBalancesRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/account-coins/balance/query', - params, - ); - } - - getAssetInfo( - params?: AssetInfoRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/transfer/asset-info/query', - params, - ); - } - - /** - * - * Wallet & Deposit Endpoints - * - */ - - /** Get Deposit Spec */ - getSupportedDepositList( - params?: SupportedDepositListRequestV3, - ): Promise> { - return this.get( - '/asset/v3/public/deposit/allowed-deposit-list/query', - params, - ); - } - - getDepositRecords( - params?: DepositRecordQueryRequestV3, - ): Promise> { - return this.getPrivate('/asset/v3/private/deposit/record/query', params); - } - - getSubDepositRecords( - params: SubDepositRecordQueryRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/deposit/sub-member-record/query', - params, - ); - } - - getWithdrawRecords( - params?: WithdrawRecordQueryRequestV3, - ): Promise> { - return this.getPrivate('/asset/v3/private/withdraw/record/query', params); - } - - getCoinInformation( - coin?: string, - ): Promise> { - return this.getPrivate('/asset/v3/private/coin-info/query', { coin }); - } - - submitWithdrawal( - params: WithdrawCreateRequestV3, - ): Promise> { - return this.postPrivate('/asset/v3/private/withdraw/create', params); - } - - cancelWithdrawal( - withdrawalId: number, - ): Promise> { - return this.postPrivate('/asset/v3/private/withdraw/create', { - withdrawalId, - }); - } - - getMasterAccountDepositAddress( - params?: QueryDepositAddressRequestV3, - ): Promise> { - return this.getPrivate('/asset/v3/private/deposit/address/query', params); - } - - getSubAccountDepositAddress( - params: QuerySubAccountDepositAddressRequestV3, - ): Promise> { - return this.getPrivate( - '/asset/v3/private/deposit/sub-member-address/query', - params, - ); - } - - createSubMember( - params: CreateSubMemberRequestV3, - ): Promise> { - return this.postPrivate('/user/v3/private/create-sub-member', params); - } - - createSubAPIKey( - params: CreateSubAPIKeyRequestV3, - ): Promise> { - return this.postPrivate('/user/v3/private/create-sub-api', params); - } - - /** - * Get Sub UID List - */ - getSubMembers(): Promise> { - return this.getPrivate('/user/v3/private/query-sub-members'); - } - - /** - * Froze Sub UID - */ - freezeSubMember( - subuid: number, - frozenStatus: 0 | 1, - ): Promise> { - return this.postPrivate('/user/v3/private/frozen-sub-member', { - subuid, - frozen: frozenStatus, - }); - } - - getAPIKeyInformation(): Promise> { - return this.getPrivate('/user/v3/private/query-api'); - } - - modifyMasterAPIKey( - params: ModifyAPIKeyRequestV3, - ): Promise> { - return this.postPrivate('/user/v3/private/update-api', params); - } - - modifySubAPIKey( - params: ModifyAPIKeyRequestV3, - ): Promise> { - return this.postPrivate('/user/v3/private/update-sub-api', params); - } - - /** WARNING: BE CAREFUL! The API key used to call this interface will be invalid immediately. */ - deleteMasterAPIKey(): Promise> { - return this.postPrivate('/user/v3/private/delete-api'); - } - - /** WARNING: BE CAREFUL! The API key used to call this interface will be invalid immediately. */ - deleteSubAPIKey(): Promise> { - return this.postPrivate('/user/v3/private/delete-sub-api'); - } -} diff --git a/src/account-asset-client.ts b/src/account-asset-client.ts deleted file mode 100644 index cd30af0..0000000 --- a/src/account-asset-client.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseWithTime, - AccountAssetInformationRequest, - DepositRecordsRequest, - EnableUniversalTransferRequest, - InternalTransferRequest, - SubAccountTransferRequest, - SupportedDepositListRequest, - TransferQueryRequest, - UniversalTransferRequest, - WithdrawalRecordsRequest, - WithdrawalRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Account Asset APIs - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class AccountAssetClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.accountAsset; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Transfer Data Endpoints - * - */ - - createInternalTransfer( - params: InternalTransferRequest, - ): Promise> { - return this.postPrivate('/asset/v1/private/transfer', params); - } - - createSubAccountTransfer( - params: SubAccountTransferRequest, - ): Promise> { - return this.postPrivate('/asset/v1/private/sub-member/transfer', params); - } - - getInternalTransfers( - params?: TransferQueryRequest, - ): Promise> { - return this.getPrivate('/asset/v1/private/transfer/list', params); - } - - getSubAccountTransfers( - params?: TransferQueryRequest, - ): Promise> { - return this.getPrivate( - '/asset/v1/private/sub-member/transfer/list', - params, - ); - } - - getSubAccounts(): Promise> { - return this.getPrivate('/asset/v1/private/sub-member/member-ids'); - } - - enableUniversalTransfer( - params?: EnableUniversalTransferRequest, - ): Promise> { - return this.postPrivate('/asset/v1/private/transferable-subs/save', params); - } - - createUniversalTransfer( - params: UniversalTransferRequest, - ): Promise> { - return this.postPrivate('/asset/v1/private/universal/transfer', params); - } - - getUniversalTransfers( - params?: TransferQueryRequest, - ): Promise> { - return this.getPrivate('/asset/v1/private/universal/transfer/list', params); - } - - /** - * - * Wallet & Deposit Endpoints - * - */ - - getSupportedDepositList( - params?: SupportedDepositListRequest, - ): Promise> { - return this.get('/asset/v1/public/deposit/allowed-deposit-list', params); - } - - getDepositRecords( - params?: DepositRecordsRequest, - ): Promise> { - return this.getPrivate('/asset/v1/private/deposit/record/query', params); - } - - getWithdrawRecords( - params?: WithdrawalRecordsRequest, - ): Promise> { - return this.getPrivate('/asset/v1/private/withdraw/record/query', params); - } - - getCoinInformation(coin?: string): Promise> { - return this.getPrivate('/asset/v1/private/coin-info/query', { coin }); - } - - getAssetInformation( - params?: AccountAssetInformationRequest, - ): Promise> { - return this.getPrivate('/asset/v1/private/asset-info/query', params); - } - - submitWithdrawal( - params: WithdrawalRequest, - ): Promise> { - return this.postPrivate('/asset/v1/private/withdraw', params); - } - - cancelWithdrawal(withdrawalId: number): Promise> { - return this.postPrivate('/asset/v1/private/withdraw/cancel', { - id: withdrawalId, - }); - } - - getDepositAddress(coin: string): Promise> { - return this.getPrivate('/asset/v1/private/deposit/address', { coin }); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } - - getApiAnnouncements(): Promise> { - return this.get('/v2/public/announcement'); - } -} diff --git a/src/contract-client.ts b/src/contract-client.ts deleted file mode 100644 index fb31fbd..0000000 --- a/src/contract-client.ts +++ /dev/null @@ -1,364 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - ContractActiveOrdersRequest, - ContractCancelOrderRequest, - ContractClosedPNLRequest, - ContractHistoricOrder, - ContractHistoricOrdersRequest, - ContractListResult, - ContractModifyOrderRequest, - ContractOrderRequest, - ContractPositionsRequest, - ContractSetAutoAddMarginRequest, - ContractSetMarginSwitchRequest, - ContractSetPositionModeRequest, - ContractSetTPSLRequest, - ContractSymbolTicker, - ContractUserExecutionHistoryRequest, - ContractWalletFundRecordRequest, - PaginatedResult, - UMCandlesRequest, - UMCategory, - UMFundingRateHistoryRequest, - UMInstrumentInfoRequest, - UMOpenInterestRequest, - UMOptionDeliveryPriceRequest, - UMPublicTradesRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Derivatives V3 Contract APIs - * @deprecated WARNING - * These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024. - * They may stop working at any point before then. - * Please update your code as soon as possible to use the V5 APIs instead. - */ -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 { - 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> { - return this.get('/derivatives/v3/public/order-book/L2', { - category, - symbol, - limit, - }); - } - - /** Get candles/klines */ - getCandles(params: UMCandlesRequest): Promise> { - return this.get('/derivatives/v3/public/kline', params); - } - - /** Get a symbol price/statistics ticker */ - getSymbolTicker( - category: UMCategory | '', - symbol?: string, - ): Promise>> { - 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> { - return this.get('/derivatives/v3/public/instruments-info', params); - } - - /** Query mark price kline (like getCandles() but for mark price). */ - getMarkPriceCandles(params: UMCandlesRequest): Promise> { - return this.get('/derivatives/v3/public/mark-price-kline', params); - } - - /** Query Index Price Kline */ - getIndexPriceCandles(params: UMCandlesRequest): Promise> { - 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> { - return this.get( - '/derivatives/v3/public/funding/history-funding-rate', - params, - ); - } - - /** Get Risk Limit */ - getRiskLimit( - category: UMCategory, - symbol: string, - ): Promise> { - return this.get('/derivatives/v3/public/risk-limit/list', { - category, - symbol, - }); - } - - /** Get option delivery price */ - getOptionDeliveryPrice( - params: UMOptionDeliveryPriceRequest, - ): Promise> { - return this.get('/derivatives/v3/public/delivery-price', params); - } - - /** Get public trading history */ - getTrades(params: UMPublicTradesRequest): Promise> { - 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> { - return this.get('/derivatives/v3/public/open-interest', params); - } - - /** - * - * Contract Account Endpoints - * - */ - - /** -> Order API */ - - /** Place an order */ - submitOrder(params: ContractOrderRequest): Promise> { - 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>> { - return this.getPrivate('/contract/v3/private/order/list', params); - } - - /** Cancel order */ - cancelOrder(params: ContractCancelOrderRequest): Promise> { - return this.postPrivate('/contract/v3/private/order/cancel', params); - } - - /** Cancel all orders */ - cancelAllOrders(symbol: string): Promise> { - 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> { - return this.postPrivate('/contract/v3/private/order/replace', params); - } - - /** Query Open Order(s) (real-time) */ - getActiveOrders( - params: ContractActiveOrdersRequest, - ): Promise> { - 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> { - return this.getPrivate('/contract/v3/private/position/list', params); - } - - /** Set auto add margin, or Auto-Margin Replenishment. */ - setAutoAddMargin( - params: ContractSetAutoAddMarginRequest, - ): Promise> { - return this.postPrivate( - '/contract/v3/private/position/set-auto-add-margin', - params, - ); - } - - /** Switch cross margin mode/isolated margin mode */ - setMarginSwitch( - params: ContractSetMarginSwitchRequest, - ): Promise> { - 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> { - return this.postPrivate( - '/contract/v3/private/position/switch-mode', - params, - ); - } - - /** - * Switch mode between Full or Partial - */ - setTPSLMode( - symbol: string, - tpSlMode: 'Full' | 'Partial', - ): Promise> { - return this.postPrivate('/contract/v3/private/position/switch-tpsl-mode', { - symbol, - tpSlMode, - }); - } - - /** Leverage setting. */ - setLeverage( - symbol: string, - buyLeverage: string, - sellLeverage: string, - ): Promise> { - 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> { - 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> { - 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> { - 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> { - return this.getPrivate('/contract/v3/private/position/closed-pnl', params); - } - - /** Get the information of open interest limit. */ - getOpenInterestLimitInfo(symbol: string): Promise> { - return this.getPrivate('/contract/v3/private/position/limit-info', { - symbol, - }); - } - - /** -> Account API */ - - /** Query wallet balance */ - getBalances(coin?: string): Promise> { - return this.getPrivate('/contract/v3/private/account/wallet/balance', { - coin, - }); - } - - /** Get user trading fee rate */ - getTradingFeeRate(symbol?: string): Promise> { - 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> { - return this.getPrivate( - '/contract/v3/private/account/wallet/fund-records', - params, - ); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } -} diff --git a/src/copy-trading-client.ts b/src/copy-trading-client.ts deleted file mode 100644 index 1fa226f..0000000 --- a/src/copy-trading-client.ts +++ /dev/null @@ -1,162 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - CopyTradingCancelOrderRequest, - CopyTradingCloseOrderRequest, - CopyTradingOrderListRequest, - CopyTradingOrderRequest, - CopyTradingTradingStopRequest, - CopyTradingTransferRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for USDC Perpetual APIs - * @deprecated WARNING - * These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024. - * They may stop working at any point before then. - * Please update your code as soon as possible to use the V5 APIs instead. - */ -export class CopyTradingClient extends BaseRestClient { - getClientType() { - // Follows the same authentication mechanism as USDC APIs - return REST_CLIENT_TYPE_ENUM.v3; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - getSymbols(): Promise> { - return this.get('/contract/v3/public/copytrading/symbol/list'); - } - - /** - * - * Account Data Endpoints - * - */ - - /** -> Order API */ - - /** Create order */ - submitOrder(params: CopyTradingOrderRequest): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/order/create', - params, - ); - } - - /** Set Trading Stop */ - setTradingStop( - params: CopyTradingTradingStopRequest, - ): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/order/trading-stop', - params, - ); - } - - /** Query Order List */ - getActiveOrders( - params?: CopyTradingOrderListRequest, - ): Promise> { - return this.getPrivate( - '/contract/v3/private/copytrading/order/list', - params, - ); - } - - /** Cancel order */ - cancelOrder( - params: CopyTradingCancelOrderRequest, - ): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/order/cancel', - params, - ); - } - - /** Close Order. - * This endpoint's rate_limit will decrease by 10 per request; - * ie, one request to this endpoint consumes 10 from the limit allowed per minute. - */ - closeOrder( - params: CopyTradingCloseOrderRequest, - ): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/order/close', - params, - ); - } - - /** -> Positions API */ - - /** Position List */ - getPositions(symbol?: string): Promise> { - return this.getPrivate('/contract/v3/private/copytrading/position/list', { - symbol, - }); - } - - /** Close Position */ - closePosition( - symbol: string, - positionIdx: string, - ): Promise> { - return this.postPrivate('/contract/v3/private/copytrading/position/close', { - symbol, - positionIdx, - }); - } - - /** Only integers can be set to set the leverage */ - setLeverage( - symbol: string, - buyLeverage: string, - sellLeverage: string, - ): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/position/set-leverage', - { symbol, buyLeverage, sellLeverage }, - ); - } - - /** - * - * Wallet Data Endpoints - * - */ - - /** Get Wallet Balance */ - getBalances(): Promise> { - return this.getPrivate('/contract/v3/private/copytrading/wallet/balance'); - } - - /** Transfer */ - transfer(params: CopyTradingTransferRequest): Promise> { - return this.postPrivate( - '/contract/v3/private/copytrading/wallet/transfer', - params, - ); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } -} diff --git a/src/index.ts b/src/index.ts index c93f1d3..2a85c6d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,5 @@ -export * from './account-asset-client'; -export * from './account-asset-client-v3'; -export * from './copy-trading-client'; -export * from './inverse-client'; -export * from './inverse-futures-client'; -export * from './linear-client'; export * from './rest-client-v5'; -export * from './spot-client'; 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'; diff --git a/src/inverse-client.ts b/src/inverse-client.ts deleted file mode 100644 index b860e17..0000000 --- a/src/inverse-client.ts +++ /dev/null @@ -1,341 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { REST_CLIENT_TYPE_ENUM } from './util'; -import { - APIResponseWithTime, - AssetExchangeRecordsReq, - CoinParam, - InverseActiveConditionalOrderRequest, - InverseActiveOrdersRequest, - InverseCancelConditionalOrderRequest, - InverseCancelOrderRequest, - InverseChangePositionMarginRequest, - InverseConditionalOrderRequest, - InverseGetClosedPnlRequest, - InverseGetOrderRequest, - InverseGetTradeRecordsRequest, - InverseOrderRequest, - InverseReplaceConditionalOrderRequest, - InverseReplaceOrderRequest, - InverseSetLeverageRequest, - InverseSetMarginTypeRequest, - InverseSetSlTpPositionModeRequest, - InverseSetTradingStopRequest, - SymbolInfo, - SymbolIntervalFromLimitParam, - SymbolLimitParam, - SymbolParam, - SymbolPeriodLimitParam, - WalletFundRecordsReq, - WithdrawRecordsReq, -} from './types'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Inverse Perpetual Futures APIs (v2) - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class InverseClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.inverse; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - getOrderBook(params: SymbolParam): Promise> { - return this.get('v2/public/orderBook/L2', params); - } - - getKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/kline/list', params); - } - - /** - * Get latest information for symbol - */ - getTickers( - params?: Partial, - ): Promise> { - return this.get('v2/public/tickers', params); - } - - getTrades(params: SymbolLimitParam): Promise> { - return this.get('v2/public/trading-records', params); - } - - getSymbols(): Promise> { - return this.get('v2/public/symbols'); - } - - getMarkPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/mark-price-kline', params); - } - - getIndexPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/index-price-kline', params); - } - - getPremiumIndexKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/premium-index-kline', params); - } - - /** - * - * Market Data : Advanced - * - */ - - getOpenInterest( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/open-interest', params); - } - - getLatestBigDeal( - params: SymbolLimitParam, - ): Promise> { - return this.get('v2/public/big-deal', params); - } - - getLongShortRatio( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/account-ratio', params); - } - - /** - * - * Account Data Endpoints - * - */ - - getApiKeyInfo(): Promise> { - return this.getPrivate('v2/private/account/api-key'); - } - - /** - * - * Wallet Data Endpoints - * - */ - - getWalletBalance( - params?: Partial, - ): Promise> { - return this.getPrivate('v2/private/wallet/balance', params); - } - - getWalletFundRecords( - params?: WalletFundRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/fund/records', params); - } - - getWithdrawRecords( - params?: WithdrawRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/withdraw/list', params); - } - - getAssetExchangeRecords( - params?: AssetExchangeRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/exchange-order/list', params); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise> { - return this.get('v2/public/time'); - } - - getApiAnnouncements(): Promise> { - return this.get('v2/public/announcement'); - } - - /** - * - * Account Data Endpoints - * - */ - - /** - * Active orders - */ - - placeActiveOrder( - orderRequest: InverseOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/order/create', orderRequest); - } - - getActiveOrderList( - params: InverseActiveOrdersRequest, - ): Promise> { - return this.getPrivate('v2/private/order/list', params); - } - - cancelActiveOrder( - params: InverseCancelOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/order/cancel', params); - } - - cancelAllActiveOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('v2/private/order/cancelAll', params); - } - - replaceActiveOrder( - params: InverseReplaceOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/order/replace', params); - } - - queryActiveOrder( - params: InverseGetOrderRequest, - ): Promise> { - return this.getPrivate('v2/private/order', params); - } - - /** - * Conditional orders - */ - - placeConditionalOrder( - params: InverseConditionalOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/stop-order/create', params); - } - - /** get conditional order list. This may see delays, use queryConditionalOrder() for real-time queries */ - getConditionalOrder( - params: InverseActiveConditionalOrderRequest, - ): Promise> { - return this.getPrivate('v2/private/stop-order/list', params); - } - - cancelConditionalOrder( - params: InverseCancelConditionalOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/stop-order/cancel', params); - } - - cancelAllConditionalOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('v2/private/stop-order/cancelAll', params); - } - - replaceConditionalOrder( - params: InverseReplaceConditionalOrderRequest, - ): Promise> { - return this.postPrivate('v2/private/stop-order/replace', params); - } - - queryConditionalOrder( - params: InverseGetOrderRequest, - ): Promise> { - return this.getPrivate('v2/private/stop-order', params); - } - - /** - * Position - */ - - getPosition( - params?: Partial, - ): Promise> { - return this.getPrivate('v2/private/position/list', params); - } - - changePositionMargin( - params: InverseChangePositionMarginRequest, - ): Promise> { - return this.postPrivate('position/change-position-margin', params); - } - - setTradingStop( - params: InverseSetTradingStopRequest, - ): Promise> { - return this.postPrivate('v2/private/position/trading-stop', params); - } - - setUserLeverage( - params: InverseSetLeverageRequest, - ): Promise> { - return this.postPrivate('v2/private/position/leverage/save', params); - } - - getTradeRecords( - params: InverseGetTradeRecordsRequest, - ): Promise> { - return this.getPrivate('v2/private/execution/list', params); - } - - getClosedPnl( - params: InverseGetClosedPnlRequest, - ): Promise> { - return this.getPrivate('v2/private/trade/closed-pnl/list', params); - } - - setSlTpPositionMode( - params: InverseSetSlTpPositionModeRequest, - ): Promise> { - return this.postPrivate('v2/private/tpsl/switch-mode', params); - } - - setMarginType( - params: InverseSetMarginTypeRequest, - ): Promise> { - return this.postPrivate('v2/private/position/switch-isolated', params); - } - - /** - * Funding - */ - - getLastFundingRate(params: SymbolParam): Promise> { - return this.get('v2/public/funding/prev-funding-rate', params); - } - - getMyLastFundingFee(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/funding/prev-funding', params); - } - - getPredictedFunding(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/funding/predicted-funding', params); - } - - /** - * LCP Info - */ - - getLcpInfo(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/account/lcp', params); - } -} diff --git a/src/inverse-futures-client.ts b/src/inverse-futures-client.ts deleted file mode 100644 index eb8646b..0000000 --- a/src/inverse-futures-client.ts +++ /dev/null @@ -1,407 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils'; -import { - APIResponseWithTime, - AssetExchangeRecordsReq, - CoinParam, - SymbolInfo, - SymbolIntervalFromLimitParam, - SymbolLimitParam, - SymbolParam, - SymbolPeriodLimitParam, - WalletFundRecordsReq, - WithdrawRecordsReq, -} from './types/shared'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Inverse Futures APIs (e.g. quarterly futures) (v2) - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class InverseFuturesClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.inverseFutures; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - getOrderBook(params: SymbolParam): Promise> { - return this.get('v2/public/orderBook/L2', params); - } - - getKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/kline/list', params); - } - - /** - * Get latest information for symbol - */ - getTickers( - params?: Partial, - ): Promise> { - return this.get('v2/public/tickers', params); - } - - /** - * Public trading records - */ - getTrades(params: SymbolLimitParam): Promise> { - return this.get('v2/public/trading-records', params); - } - - getSymbols(): Promise> { - return this.get('v2/public/symbols'); - } - - getMarkPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/mark-price-kline', params); - } - - getIndexPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/index-price-kline', params); - } - - getPremiumIndexKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('v2/public/premium-index-kline', params); - } - - /** - * - * Market Data : Advanced - * - */ - - getOpenInterest( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/open-interest', params); - } - - getLatestBigDeal( - params: SymbolLimitParam, - ): Promise> { - return this.get('v2/public/big-deal', params); - } - - getLongShortRatio( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/account-ratio', params); - } - - /** - * - * Account Data Endpoints - * - */ - - getApiKeyInfo(): Promise> { - return this.getPrivate('v2/private/account/api-key'); - } - - /** - * - * Wallet Data Endpoints - * - */ - - getWalletBalance( - params?: Partial, - ): Promise> { - return this.getPrivate('v2/private/wallet/balance', params); - } - - getWalletFundRecords( - params?: WalletFundRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/fund/records', params); - } - - getWithdrawRecords( - params?: WithdrawRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/withdraw/list', params); - } - - getAssetExchangeRecords( - params?: AssetExchangeRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/exchange-order/list', params); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise> { - return this.get('v2/public/time'); - } - - getApiAnnouncements(): Promise> { - return this.get('v2/public/announcement'); - } - - /** - * - * Account Data Endpoints - * - */ - - /** - * Active orders - */ - - placeActiveOrder(orderRequest: { - side: string; - symbol: string; - order_type: string; - qty: number; - price?: number; - time_in_force: string; - take_profit?: number; - stop_loss?: number; - reduce_only?: boolean; - close_on_trigger?: boolean; - order_link_id?: string; - }): Promise> { - return this.postPrivate('futures/private/order/create', orderRequest); - } - - getActiveOrderList(params: { - symbol: string; - order_status?: string; - direction?: string; - limit?: number; - cursor?: string; - }): Promise> { - return this.getPrivate('futures/private/order/list', params); - } - - cancelActiveOrder(params: { - symbol: string; - order_id?: string; - order_link_id?: string; - }): Promise> { - return this.postPrivate('futures/private/order/cancel', params); - } - - cancelAllActiveOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('futures/private/order/cancelAll', params); - } - - replaceActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: string; - p_r_price?: string; - }): Promise> { - return this.postPrivate('futures/private/order/replace', params); - } - - queryActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - }): Promise> { - return this.getPrivate('futures/private/order', params); - } - - /** - * Conditional orders - */ - - placeConditionalOrder(params: { - side: string; - symbol: string; - order_type: string; - qty: string; - price?: string; - base_price: string; - stop_px: string; - time_in_force: string; - trigger_by?: string; - close_on_trigger?: boolean; - order_link_id?: string; - }): Promise> { - return this.postPrivate('futures/private/stop-order/create', params); - } - - getConditionalOrder(params: { - symbol: string; - stop_order_status?: string; - direction?: string; - limit?: number; - cursor?: string; - }): Promise> { - return this.getPrivate('futures/private/stop-order/list', params); - } - - cancelConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): Promise> { - return this.postPrivate('futures/private/stop-order/cancel', params); - } - - cancelAllConditionalOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('futures/private/stop-order/cancelAll', params); - } - - replaceConditionalOrder(params: { - stop_order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: number; - p_r_price?: string; - p_r_trigger_price?: string; - }): Promise> { - return this.postPrivate('futures/private/stop-order/replace', params); - } - - queryConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): Promise> { - return this.getPrivate('futures/private/stop-order', params); - } - - /** - * Position - */ - - /** - * Get position list - */ - getPosition( - params?: Partial, - ): Promise> { - return this.getPrivate('futures/private/position/list', params); - } - - changePositionMargin(params: { - symbol: string; - margin: string; - }): Promise> { - return this.postPrivate( - 'futures/private/position/change-position-margin', - params, - ); - } - - setTradingStop(params: { - symbol: string; - take_profit?: number; - stop_loss?: number; - trailing_stop?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - new_trailing_active?: number; - }): Promise> { - return this.postPrivate('futures/private/position/trading-stop', params); - } - - setUserLeverage(params: { - symbol: string; - buy_leverage: number; - sell_leverage: number; - }): Promise> { - return this.postPrivate('futures/private/position/leverage/save', params); - } - - /** - * Position mode switch - */ - setPositionMode(params: { - symbol: string; - mode: number; - }): Promise> { - return this.postPrivate('futures/private/position/switch-mode', params); - } - - /** - * Cross/Isolated margin switch. Must set leverage value when switching. - */ - setMarginType(params: { - symbol: string; - is_isolated: boolean; - buy_leverage: number; - sell_leverage: number; - }): Promise> { - return this.postPrivate('futures/private/position/switch-isolated', params); - } - - getTradeRecords(params: { - order_id?: string; - symbol: string; - start_time?: number; - page?: number; - limit?: number; - order?: string; - }): Promise> { - return this.getPrivate('futures/private/execution/list', params); - } - - getClosedPnl(params: { - symbol: string; - start_time?: number; - end_time?: number; - exec_type?: string; - page?: number; - limit?: number; - }): Promise> { - return this.getPrivate('futures/private/trade/closed-pnl/list', params); - } - - /** - * Funding - */ - - getLastFundingRate(params: SymbolParam): Promise> { - return this.get('v2/public/funding/prev-funding-rate', params); - } - - getMyLastFundingFee(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/funding/prev-funding', params); - } - - getPredictedFunding(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/funding/predicted-funding', params); - } - - /** - * LCP Info - */ - - getLcpInfo(params: SymbolParam): Promise> { - return this.getPrivate('v2/private/account/lcp', params); - } -} diff --git a/src/linear-client.ts b/src/linear-client.ts deleted file mode 100644 index 2e6a637..0000000 --- a/src/linear-client.ts +++ /dev/null @@ -1,391 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils'; -import { - APIResponse, - APIResponseWithTime, - AssetExchangeRecordsReq, - CoinParam, - LinearCancelConditionalOrderRequest, - LinearCancelOrderRequest, - LinearConditionalOrderRequest, - LinearGetClosedPnlRequest, - LinearGetConditionalOrderRequest, - LinearGetHistoryTradeRecordsRequest, - LinearGetOrderRequest, - LinearGetOrdersRequest, - LinearGetTradeRecordsRequest, - LinearOrder, - LinearQueryConditionalOrderRequest, - LinearReplaceConditionalOrderRequest, - LinearReplaceOrderRequest, - LinearSetAddReduceMarginRequest, - LinearSetAutoAddMarginRequest, - LinearSetMarginSwitchRequest, - LinearSetPositionModeRequest, - LinearSetPositionTpSlModeRequest, - LinearSetRiskLimitRequest, - LinearSetTradingStopRequest, - LinearSetUserLeverageRequest, - NewLinearOrder, - PerpPosition, - PerpPositionRoot, - SymbolInfo, - SymbolIntervalFromLimitParam, - SymbolLimitParam, - SymbolParam, - SymbolPeriodLimitParam, - WalletBalances, - WalletFundRecordsReq, - WithdrawRecordsReq, -} from './types'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for linear/USD perpetual futures APIs (v2) - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class LinearClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.linear; - } - - async fetchServerTime(): Promise { - const timeRes = await this.getServerTime(); - return Number(timeRes.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - getOrderBook(params: SymbolParam): Promise> { - return this.get('v2/public/orderBook/L2', params); - } - - getKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('public/linear/kline', params); - } - - /** - * Get latest information for symbol - */ - getTickers( - params?: Partial, - ): Promise> { - return this.get('v2/public/tickers', params); - } - - getTrades(params: SymbolLimitParam): Promise> { - return this.get('public/linear/recent-trading-records', params); - } - - getSymbols(): Promise> { - return this.get('v2/public/symbols'); - } - - getLastFundingRate(params: SymbolParam): Promise> { - return this.get('public/linear/funding/prev-funding-rate', params); - } - - getMarkPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('public/linear/mark-price-kline', params); - } - - getIndexPriceKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('public/linear/index-price-kline', params); - } - - getPremiumIndexKline( - params: SymbolIntervalFromLimitParam, - ): Promise> { - return this.get('public/linear/premium-index-kline', params); - } - - /** - * - * Market Data : Advanced - * - */ - - getOpenInterest( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/open-interest', params); - } - - getLatestBigDeal( - params: SymbolLimitParam, - ): Promise> { - return this.get('v2/public/big-deal', params); - } - - getLongShortRatio( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('v2/public/account-ratio', params); - } - - /** - * - * Account Data Endpoints - * - */ - - getApiKeyInfo(): Promise> { - return this.getPrivate('v2/private/account/api-key'); - } - - /** - * - * Wallet Data Endpoints - * - */ - - getWalletBalance( - params?: Partial, - ): Promise> { - return this.getPrivate('v2/private/wallet/balance', params); - } - - getWalletFundRecords( - params?: WalletFundRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/fund/records', params); - } - - getWithdrawRecords( - params?: WithdrawRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/wallet/withdraw/list', params); - } - - getAssetExchangeRecords( - params?: AssetExchangeRecordsReq, - ): Promise> { - return this.getPrivate('v2/private/exchange-order/list', params); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise> { - return this.get('v2/public/time'); - } - - getApiAnnouncements(): Promise> { - return this.get('v2/public/announcement'); - } - - /** - * - * Account Data Endpoints - * - */ - - placeActiveOrder( - params: NewLinearOrder, - ): Promise> { - return this.postPrivate('private/linear/order/create', params); - } - - getActiveOrderList( - params: LinearGetOrdersRequest, - ): Promise> { - return this.getPrivate('private/linear/order/list', params); - } - - cancelActiveOrder( - params: LinearCancelOrderRequest, - ): Promise> { - return this.postPrivate('private/linear/order/cancel', params); - } - - cancelAllActiveOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('private/linear/order/cancel-all', params); - } - - replaceActiveOrder( - params: LinearReplaceOrderRequest, - ): Promise> { - return this.postPrivate('private/linear/order/replace', params); - } - - queryActiveOrder( - params: LinearGetOrderRequest, - ): Promise> { - return this.getPrivate('private/linear/order/search', params); - } - - /** - * Conditional orders - */ - - placeConditionalOrder( - params: LinearConditionalOrderRequest, - ): Promise> { - return this.postPrivate('private/linear/stop-order/create', params); - } - - getConditionalOrder( - params: LinearGetConditionalOrderRequest, - ): Promise> { - return this.getPrivate('private/linear/stop-order/list', params); - } - - cancelConditionalOrder( - params: LinearCancelConditionalOrderRequest, - ): Promise> { - return this.postPrivate('private/linear/stop-order/cancel', params); - } - - cancelAllConditionalOrders( - params: SymbolParam, - ): Promise> { - return this.postPrivate('private/linear/stop-order/cancel-all', params); - } - - replaceConditionalOrder( - params: LinearReplaceConditionalOrderRequest, - ): Promise> { - return this.postPrivate('private/linear/stop-order/replace', params); - } - - queryConditionalOrder( - params: LinearQueryConditionalOrderRequest, - ): Promise> { - return this.getPrivate('private/linear/stop-order/search', params); - } - - /** - * Position - */ - - getPosition(): Promise>; - - getPosition( - params: Partial, - ): Promise>; - - getPosition( - params?: Partial, - ): Promise> { - return this.getPrivate('private/linear/position/list', params); - } - - setAutoAddMargin( - params?: LinearSetAutoAddMarginRequest, - ): Promise> { - return this.postPrivate( - 'private/linear/position/set-auto-add-margin', - params, - ); - } - - setMarginSwitch( - params?: LinearSetMarginSwitchRequest, - ): Promise> { - return this.postPrivate('private/linear/position/switch-isolated', params); - } - - /** - * Switch between one-way vs hedge mode. Use `linearPositionModeEnum` for the mode parameter. - */ - setPositionMode( - params: LinearSetPositionModeRequest, - ): Promise> { - return this.postPrivate('private/linear/position/switch-mode', params); - } - - /** - * Switch TP/SL mode between full or partial. When set to Partial, TP/SL orders may have a quantity less than the position size. - * This is set with the setTradingStop() method. Use `positionTpSlModeEnum` for the tp_sl_mode parameter. - */ - setPositionTpSlMode( - params: LinearSetPositionTpSlModeRequest, - ): Promise> { - return this.postPrivate('private/linear/tpsl/switch-mode', params); - } - - setAddReduceMargin( - params?: LinearSetAddReduceMarginRequest, - ): Promise> { - return this.postPrivate('private/linear/position/add-margin', params); - } - - setUserLeverage( - params: LinearSetUserLeverageRequest, - ): Promise> { - return this.postPrivate('private/linear/position/set-leverage', params); - } - - setTradingStop( - params: LinearSetTradingStopRequest, - ): Promise> { - return this.postPrivate('private/linear/position/trading-stop', params); - } - - getTradeRecords( - params: LinearGetTradeRecordsRequest, - ): Promise> { - return this.getPrivate('private/linear/trade/execution/list', params); - } - - getHistoryTradeRecords( - params: LinearGetHistoryTradeRecordsRequest, - ): Promise> { - return this.getPrivate( - '/private/linear/trade/execution/history-list', - params, - ); - } - - getClosedPnl( - params: LinearGetClosedPnlRequest, - ): Promise> { - return this.getPrivate('private/linear/trade/closed-pnl/list', params); - } - - /** - * Risk Limit - */ - - getRiskLimitList(params: SymbolParam): Promise> { - return this.getPrivate('public/linear/risk-limit', params); - } - - setRiskLimit( - params: LinearSetRiskLimitRequest, - ): Promise> { - return this.postPrivate('private/linear/position/set-risk', params); - } - - /** - * Funding - */ - - getPredictedFundingFee( - params: SymbolParam, - ): Promise> { - return this.getPrivate('private/linear/funding/predicted-funding', params); - } - - getLastFundingFee(params: SymbolParam): Promise> { - return this.getPrivate('private/linear/funding/prev-funding', params); - } -} diff --git a/src/spot-client-v3.ts b/src/spot-client-v3.ts index 4bc1e8f..8c8d0f4 100644 --- a/src/spot-client-v3.ts +++ b/src/spot-client-v3.ts @@ -1,17 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - KlineInterval, - NewSpotOrderV3, - SpotBalances, - SpotCancelOrderBatchRequest, - SpotCrossMarginBorrowingInfoRequest, - SpotCrossMarginRepaymentHistoryRequest, - SpotLeveragedTokenPRHistoryRequest, - SpotMyTradesRequest, - SpotOrderQueryById, -} from './types'; +import { APIResponseV3, numberInString } from './types'; import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; @@ -39,17 +27,11 @@ export class SpotClientV3 extends BaseRestClient { * */ - /** Get all symbols */ - getSymbols(): Promise> { - return this.get('/spot/v3/public/symbols'); - } - - /** Get orderbook for symbol */ - getOrderBook(symbol: string, limit?: number): Promise> { - return this.get('/spot/v3/public/quote/depth', { symbol, limit }); - } - - /** Get merged orderbook for symbol */ + /** + * Get merged orderbook for symbol + * + * This is the only known pre-V5 endpoint to still be online. + */ getMergedOrderBook( symbol: string, scale?: number, @@ -62,246 +44,13 @@ export class SpotClientV3 extends BaseRestClient { }); } - /** Get public trading records (raw trades) */ - getTrades(symbol: string, limit?: number): Promise> { - return this.get('/spot/v3/public/quote/trades', { symbol, limit }); - } - - /** Get candles/klines */ - getCandles( - symbol: string, - interval: KlineInterval, - limit?: number, - startTime?: number, - endTime?: number, - ): Promise> { - return this.get('/spot/v3/public/quote/kline', { - symbol, - interval, - limit, - startTime, - endTime, - }); - } - - /** Get latest information for symbol (24hr ticker) */ - get24hrTicker(symbol?: string): Promise> { - return this.get('/spot/v3/public/quote/ticker/24hr', { symbol }); - } - - /** Get last traded price */ - getLastTradedPrice(symbol?: string): Promise> { - return this.get('/spot/v3/public/quote/ticker/price', { symbol }); - } - - /** Get best bid/ask price */ - getBestBidAskPrice(symbol?: string): Promise> { - return this.get('/spot/v3/public/quote/ticker/bookTicker', { symbol }); - } - - /** - * - * Account Data Endpoints - * - */ - - /** -> Order API */ - - /** Create order */ - submitOrder(params: NewSpotOrderV3): Promise> { - return this.postPrivate('/spot/v3/private/order', params); - } - - /** Get active order state */ - getOrder(params: SpotOrderQueryById): Promise> { - return this.getPrivate('/spot/v3/private/order', params); - } - - /** Cancel order */ - cancelOrder(params: SpotOrderQueryById): Promise> { - return this.postPrivate('/spot/v3/private/cancel-order', params); - } - - /** Batch cancel orders */ - cancelOrderBatch( - params: SpotCancelOrderBatchRequest, - ): Promise> { - const orderTypes = params.orderTypes - ? params.orderTypes.join(',') - : undefined; - - return this.postPrivate('/spot/v3/private/cancel-orders', { - ...params, - orderTypes, - }); - } - - /** Batch cancel up to 100 orders by ID */ - cancelOrderBatchIDs(orderIds: string[]): Promise> { - const orderIdsCsv = orderIds.join(','); - return this.postPrivate('/spot/v3/private/cancel-orders-by-ids', { - orderIds: orderIdsCsv, - }); - } - - /** Get open orders */ - getOpenOrders( - symbol?: string, - orderId?: string, - limit?: number, - orderCategory?: 0 | 1, - ): Promise> { - return this.getPrivate('/spot/v3/private/open-orders', { - symbol, - orderId, - limit, - orderCategory, - }); - } - - /** Get order history */ - getPastOrders( - symbol?: string, - orderId?: string, - limit?: number, - orderCategory?: 0 | 1, - ): Promise> { - return this.getPrivate('/spot/v3/private/history-orders', { - symbol, - orderId, - limit, - orderCategory, - }); - } - - /** - * Get your trade history. - * If startTime is not specified, you can only query for records in the last 7 days. - * If you want to query for records older than 7 days, startTime is required. - */ - getMyTrades(params?: SpotMyTradesRequest): Promise> { - return this.getPrivate('/spot/v3/private/my-trades', params); - } - - /** - * - * Wallet Data Endpoints - * - */ - - /** Get Wallet Balance */ - getBalances(): Promise> { - return this.getPrivate('/spot/v3/private/account'); - } - /** * * API Data Endpoints * */ - getServerTime(): Promise { + getServerTime(): Promise<{ time_now: numberInString }> { return this.get('/v2/public/time'); } - - /** - * - * Leveraged Token Endpoints - * - */ - - /** Get all asset infos */ - getLeveragedTokenAssetInfos(ltCode?: string): Promise> { - return this.get('/spot/v3/public/infos', { ltCode }); - } - - /** Get leveraged token market info */ - getLeveragedTokenMarketInfo(ltCode: string): Promise> { - return this.getPrivate('/spot/v3/private/reference', { ltCode }); - } - - /** Purchase leveraged token */ - purchaseLeveragedToken( - ltCode: string, - ltAmount: string, - serialNo?: string, - ): Promise> { - return this.postPrivate('/spot/v3/private/purchase', { - ltCode, - ltAmount, - serialNo, - }); - } - - /** Redeem leveraged token */ - redeemLeveragedToken( - ltCode: string, - ltAmount: string, - serialNo?: string, - ): Promise> { - return this.postPrivate('/spot/v3/private/redeem', { - ltCode, - ltAmount, - serialNo, - }); - } - - /** Get leveraged token purchase/redemption history */ - getLeveragedTokenPRHistory( - params?: SpotLeveragedTokenPRHistoryRequest, - ): Promise> { - return this.getPrivate('/spot/v3/private/record', params); - } - - /** - * - * Cross Margin Trading Endpoints - * - */ - - /** Borrow margin loan */ - borrowCrossMarginLoan( - coin: string, - qty: string, - ): Promise> { - return this.postPrivate('/spot/v3/private/cross-margin-loan', { - coin, - qty, - }); - } - - /** Repay margin loan */ - repayCrossMarginLoan(coin: string, qty: string): Promise> { - return this.postPrivate('/spot/v3/private/cross-margin-repay', { - coin, - qty, - }); - } - - /** Query borrowing info */ - getCrossMarginBorrowingInfo( - params?: SpotCrossMarginBorrowingInfoRequest, - ): Promise> { - return this.getPrivate('/spot/v3/private/cross-margin-orders', params); - } - - /** Query account info */ - getCrossMarginAccountInfo(): Promise> { - return this.getPrivate('/spot/v3/private/cross-margin-account'); - } - - /** Query interest & quota */ - getCrossMarginInterestQuota(coin: string): Promise> { - return this.getPrivate('/spot/v3/private/cross-margin-loan-info', { coin }); - } - - /** Query repayment history */ - getCrossMarginRepaymentHistory( - params?: SpotCrossMarginRepaymentHistoryRequest, - ): Promise> { - return this.getPrivate( - '/spot/v3/private/cross-margin-repay-history', - params, - ); - } } diff --git a/src/spot-client.ts b/src/spot-client.ts deleted file mode 100644 index c18ae0c..0000000 --- a/src/spot-client.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponse, - KlineInterval, - NewSpotOrder, - OrderSide, - OrderTypeSpot, - SpotBalances, - SpotLastPrice, - SpotOrderQueryById, - SpotSymbolInfo, -} from './types'; -import BaseRestClient from './util/BaseRestClient'; -import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils'; - -/** - * REST API client for Spot APIs (v1) - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class SpotClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.spot; - } - - fetchServerTime(): Promise { - return this.getServerTime(); - } - - async getServerTime(): Promise { - const res = await this.get('/spot/v1/time'); - return res.result.serverTime; - } - - /** - * - * Market Data Endpoints - * - **/ - - getSymbols(): Promise> { - return this.get('/spot/v1/symbols'); - } - - getOrderBook(symbol: string, limit?: number): Promise> { - return this.get('/spot/quote/v1/depth', { - symbol, - limit, - }); - } - - getMergedOrderBook( - symbol: string, - scale?: number, - limit?: number, - ): Promise> { - return this.get('/spot/quote/v1/depth/merged', { - symbol, - scale, - limit, - }); - } - - getTrades(symbol: string, limit?: number): Promise> { - return this.get('/spot/quote/v1/trades', { - symbol, - limit, - }); - } - - getCandles( - symbol: string, - interval: KlineInterval, - limit?: number, - startTime?: number, - endTime?: number, - ): Promise> { - return this.get('/spot/quote/v1/kline', { - symbol, - interval, - limit, - startTime, - endTime, - }); - } - - get24hrTicker(symbol?: string): Promise> { - return this.get('/spot/quote/v1/ticker/24hr', { symbol }); - } - - getLastTradedPrice(): Promise>; - - getLastTradedPrice(symbol: string): Promise>; - - getLastTradedPrice( - symbol?: string, - ): Promise> { - return this.get('/spot/quote/v1/ticker/price', { symbol }); - } - - getBestBidAskPrice(symbol?: string): Promise> { - return this.get('/spot/quote/v1/ticker/book_ticker', { symbol }); - } - - /** - * Account Data Endpoints - */ - - submitOrder(params: NewSpotOrder): Promise> { - return this.postPrivate('/spot/v1/order', params); - } - - getOrder(params: SpotOrderQueryById): Promise> { - return this.getPrivate('/spot/v1/order', params); - } - - cancelOrder(params: SpotOrderQueryById): Promise> { - return this.deletePrivate('/spot/v1/order', params); - } - - cancelOrderBatch(params: { - symbol: string; - side?: OrderSide; - orderTypes: OrderTypeSpot[]; - }): Promise> { - const orderTypes = params.orderTypes - ? params.orderTypes.join(',') - : undefined; - - return this.deletePrivate('/spot/order/batch-cancel', { - ...params, - orderTypes, - }); - } - - getOpenOrders( - symbol?: string, - orderId?: string, - limit?: number, - ): Promise> { - return this.getPrivate('/spot/v1/open-orders', { - symbol, - orderId, - limit, - }); - } - - getPastOrders( - symbol?: string, - orderId?: string, - limit?: number, - ): Promise> { - return this.getPrivate('/spot/v1/history-orders', { - symbol, - orderId, - limit, - }); - } - - getMyTrades( - symbol?: string, - limit?: number, - fromId?: number, - toId?: number, - ): Promise> { - return this.getPrivate('/spot/v1/myTrades', { - symbol, - limit, - fromId, - toId, - }); - } - - /** - * Wallet Data Endpoints - */ - - getBalances(): Promise> { - return this.getPrivate('/spot/v1/account'); - } -} diff --git a/src/types/request/index.ts b/src/types/request/index.ts index 40bd984..1c6b741 100644 --- a/src/types/request/index.ts +++ b/src/types/request/index.ts @@ -3,7 +3,6 @@ export * from './copy-trading'; export * from './contract'; export * from './linear'; export * from './inverse'; -export * from './spot'; export * from './usdc-perp'; export * from './usdc-options'; export * from './usdc-shared'; diff --git a/src/types/request/spot.ts b/src/types/request/spot.ts deleted file mode 100644 index ec90dc1..0000000 --- a/src/types/request/spot.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { OrderSide, numberInString } from '../shared'; - -export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER'; -export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC'; - -export interface NewSpotOrder { - symbol: string; - qty: number; - side: OrderSide; - type: OrderTypeSpot; - timeInForce?: OrderTimeInForce; - price?: number; - orderLinkId?: string; -} - -export interface NewSpotOrderV3 { - symbol: string; - orderQty: string; - side: OrderSide; - orderType: OrderTypeSpot; - timeInForce?: OrderTimeInForce; - orderPrice?: string; - orderLinkId?: string; - orderCategory?: 0 | 1; - triggerPrice?: string; -} - -export interface SpotCancelOrderBatchRequest { - symbol: string; - side?: OrderSide; - orderTypes: OrderTypeSpot[]; - orderCategory?: 0 | 1; -} - -export interface SpotOrderQueryById { - orderId?: string; - orderLinkId?: string; - orderCategory?: 0 | 1; -} - -export interface SpotSymbolInfo { - name: string; - alias: string; - baseCurrency: string; - quoteCurrency: string; - basePrecision: numberInString; - quotePrecision: numberInString; - minTradeQuantity: numberInString; - minTradeAmount: numberInString; - minPricePrecision: numberInString; - maxTradeQuantity: numberInString; - maxTradeAmount: numberInString; - category: numberInString; -} - -export interface SpotMyTradesRequest { - symbol?: string; - orderId?: string; - limit?: string; - startTime?: number; - endTime?: number; - fromTradeId?: string; - toTradeId?: string; -} - -export interface SpotLeveragedTokenPRHistoryRequest { - ltCode?: string; - orderId?: string; - startTime?: number; - endTime?: number; - limit?: number; - orderType?: 1 | 2; - serialNo?: string; -} - -export interface SpotCrossMarginBorrowingInfoRequest { - startTime?: number; - endTime?: number; - coin?: string; - status?: 0 | 1 | 2; - limit?: number; -} - -export interface SpotCrossMarginRepaymentHistoryRequest { - startTime?: number; - endTime?: number; - coin?: string; - limit?: number; -} diff --git a/src/types/shared.ts b/src/types/shared.ts index 9be7676..4a25db2 100644 --- a/src/types/shared.ts +++ b/src/types/shared.ts @@ -1,23 +1,7 @@ -import { ContractClient } from '../contract-client'; -import { InverseClient } from '../inverse-client'; -import { LinearClient } from '../linear-client'; import { RestClientV5 } from '../rest-client-v5'; -import { SpotClient } from '../spot-client'; import { SpotClientV3 } from '../spot-client-v3'; -import { UnifiedMarginClient } from '../unified-margin-client'; -import { USDCOptionClient } from '../usdc-option-client'; -import { USDCPerpetualClient } from '../usdc-perpetual-client'; -export type RESTClient = - | InverseClient - | LinearClient - | SpotClient - | SpotClientV3 - | USDCOptionClient - | USDCPerpetualClient - | UnifiedMarginClient - | ContractClient - | RestClientV5; +export type RESTClient = SpotClientV3 | RestClientV5; export type numberInString = string; @@ -53,14 +37,6 @@ export type KlineIntervalV3 = | 'W' | 'M'; -export interface APIResponse { - ret_code: number; - ret_msg: 'OK' | string; - ext_code: string | null; - ext_info: string | null; - result: T; -} - export interface APIRateLimit { /** Remaining requests to this endpoint before the next reset */ remainingRequests: number; @@ -88,12 +64,6 @@ export interface APIResponseV3 { } export type APIResponseV3WithTime = APIResponseV3 & { time: number }; - -export interface APIResponseWithTime extends APIResponse { - /** UTC timestamp */ - time_now: numberInString; -} - /** * Request Parameter Types */ diff --git a/src/types/websockets/ws-general.ts b/src/types/websockets/ws-general.ts index e156ad9..bd4d52e 100644 --- a/src/types/websockets/ws-general.ts +++ b/src/types/websockets/ws-general.ts @@ -1,18 +1,7 @@ import { RestClientOptions, WS_KEY_MAP } from '../../util'; /** For spot markets, spotV3 is recommended */ -export type APIMarket = - | 'inverse' - | 'linear' - | 'spot' - | 'spotv3' - | 'usdcOption' - | 'usdcPerp' - | 'unifiedPerp' - | 'unifiedOption' - | 'contractUSDT' - | 'contractInverse' - | 'v5'; +export type APIMarket = 'v5'; // Same as inverse futures export type WsPublicInverseTopic = @@ -111,7 +100,7 @@ export interface WSClientConfigurableOptions { /** * The API group this client should connect to. The V5 market is currently used by default. * - * For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account asset/copy trading) + * Only the "V5" "market" is supported here. */ market?: APIMarket; @@ -128,6 +117,7 @@ export interface WSClientConfigurableOptions { reconnectTimeout?: number; restOptions?: RestClientOptions; + // eslint-disable-next-line @typescript-eslint/no-explicit-any requestOptions?: any; diff --git a/src/unified-margin-client.ts b/src/unified-margin-client.ts deleted file mode 100644 index bbfce4a..0000000 --- a/src/unified-margin-client.ts +++ /dev/null @@ -1,401 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - InternalTransferRequest, - UM7DayTradingHistoryRequest, - UMActiveOrdersRequest, - UMBatchOrder, - UMBatchOrderCancel, - UMBatchOrderReplace, - UMBorrowHistoryRequest, - UMCancelAllOrdersRequest, - UMCancelOrderRequest, - UMCandlesRequest, - UMCategory, - UMExchangeCoinsRequest, - UMFundingRateHistoryRequest, - UMHistoricOrder, - UMHistoricOrdersRequest, - UMInstrumentInfo, - UMInstrumentInfoRequest, - UMModifyOrderRequest, - UMOpenInterestRequest, - UMOptionDeliveryPriceRequest, - UMOptionsSettlementHistoryRequest, - UMOrderRequest, - UMPaginatedResult, - UMPerpSettlementHistoryRequest, - UMPositionsRequest, - UMPublicTradesRequest, - UMSetTPSLRequest, - UMTransactionLogRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for Derivatives V3 unified margin APIs - * @deprecated WARNING - * These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024. - * They may stop working at any point before then. - * Please update your code as soon as possible to use the V5 APIs instead. - */ -export class UnifiedMarginClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.v3; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - /** Query order book info. Each side has a depth of 25 orders. */ - getOrderBook( - symbol: string, - category: string, - limit?: number, - ): Promise> { - return this.get('/derivatives/v3/public/order-book/L2', { - category, - symbol, - limit, - }); - } - - /** Get candles/klines */ - getCandles(params: UMCandlesRequest): Promise> { - return this.get('/derivatives/v3/public/kline', params); - } - - /** Get a symbol price/statistics ticker */ - getSymbolTicker( - category: UMCategory, - symbol?: string, - ): Promise> { - 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>> { - return this.get('/derivatives/v3/public/instruments-info', params); - } - - /** Query mark price kline (like getCandles() but for mark price). */ - getMarkPriceCandles(params: UMCandlesRequest): Promise> { - return this.get('/derivatives/v3/public/mark-price-kline', params); - } - - /** Query Index Price Kline */ - getIndexPriceCandles(params: UMCandlesRequest): Promise> { - 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> { - return this.get( - '/derivatives/v3/public/funding/history-funding-rate', - params, - ); - } - - /** Get Risk Limit */ - getRiskLimit( - category: UMCategory, - symbol: string, - ): Promise> { - return this.get('/derivatives/v3/public/risk-limit/list', { - category, - symbol, - }); - } - - /** Get option delivery price */ - getOptionDeliveryPrice( - params: UMOptionDeliveryPriceRequest, - ): Promise> { - return this.get('/derivatives/v3/public/delivery-price', params); - } - - /** Get recent trades */ - getTrades(params: UMPublicTradesRequest): Promise> { - 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> { - return this.get('/derivatives/v3/public/open-interest', params); - } - - /** - * - * Unified Margin Account Endpoints - * - */ - - /** -> Order API */ - - /** Place an order */ - submitOrder(params: UMOrderRequest): Promise> { - return this.postPrivate('/unified/v3/private/order/create', params); - } - - /** 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: UMModifyOrderRequest): Promise> { - return this.postPrivate('/unified/v3/private/order/replace', params); - } - - /** Cancel order */ - cancelOrder(params: UMCancelOrderRequest): Promise> { - return this.postPrivate('/unified/v3/private/order/cancel', params); - } - - /** Query Open Orders */ - getActiveOrders(params: UMActiveOrdersRequest): Promise> { - return this.getPrivate('/unified/v3/private/order/unfilled-orders', 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: UMHistoricOrdersRequest, - ): Promise>> { - return this.getPrivate('/unified/v3/private/order/list', params); - } - - /** - * This API provides the batch order mode under the unified margin account. - * Max 10 per request - */ - batchSubmitOrders( - category: UMCategory, - orders: UMBatchOrder[], - ): Promise> { - return this.postPrivate('/unified/v3/private/order/create-batch', { - category, - request: orders, - }); - } - - /** - * This interface can modify the open order information in batches. - * Currently, it is not supported to modify the conditional order information. - * Please note that only unfilled or partial filled orders can be modified. - * If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type - */ - batchReplaceOrders( - category: UMCategory, - orders: UMBatchOrderReplace[], - ): Promise> { - return this.postPrivate('/unified/v3/private/order/replace-batch', { - category, - request: orders, - }); - } - - /** - * This API provides batch cancellation under the unified margin account. - * Order cancellation of futures and options cannot be canceled in one request at the same time. - * If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type. - */ - batchCancelOrders( - category: UMCategory, - orders: UMBatchOrderCancel[], - ): Promise> { - return this.postPrivate('/unified/v3/private/order/cancel-batch', { - category, - request: orders, - }); - } - - /** - * This API provides the cancellation of all open orders under the unified margin account. - * Order cancellation of futures and options cannot be canceled in one request at the same time. - * If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type. - */ - cancelAllOrders( - params: UMCancelAllOrdersRequest, - ): Promise> { - return this.postPrivate('/unified/v3/private/order/cancel-all', params); - } - - /** -> Positions API */ - - /** - * Query my positions real-time. Accessing personal list of positions. - * Users can access their position holding information through this interface, such as the number of position holdings and wallet balance. - */ - getPositions(params: UMPositionsRequest): Promise> { - return this.getPrivate('/unified/v3/private/position/list', params); - } - - /** Leverage setting. */ - setLeverage( - category: UMCategory, - symbol: string, - buyLeverage: number, - sellLeverage: number, - ): Promise> { - return this.postPrivate('/unified/v3/private/position/set-leverage', { - category, - symbol, - buyLeverage, - sellLeverage, - }); - } - - /** - * Switching the TP/SL mode to the cross margin mode or selected positions. - * When you set the TP/SL mode on the selected positions, the quantity of take-profit or stop-loss orders can be smaller than the position size. Please use Trading-Stop endpoint. - */ - setTPSLMode( - category: UMCategory, - symbol: string, - tpSlMode: 1 | 0, - ): Promise> { - return this.postPrivate('/unified/v3/private/position/tpsl/switch-mode', { - category, - symbol, - tpSlMode, - }); - } - - /** Set risk limit */ - setRiskLimit( - category: UMCategory, - symbol: string, - riskId: number, - positionIdx: number, - ): Promise> { - return this.postPrivate('/unified/v3/private/position/set-risk-limit', { - category, - symbol, - riskId, - positionIdx, - }); - } - - /** - * Set position TP/SL and trailing stop. - * Pass the following parameters, then the system will create conditional orders. - * If the position is closed, the system will cancel these orders, and adjust the position size. - */ - setTPSL(params: UMSetTPSLRequest): Promise> { - return this.postPrivate( - '/unified/v3/private/position/trading-stop', - params, - ); - } - - /** - * Access the user's filled history, ranked by time in descending order. - * There might be multiple filled histories for an order. - */ - get7DayTradingHistory( - params: UM7DayTradingHistoryRequest, - ): Promise> { - return this.getPrivate('/unified/v3/private/execution/list', params); - } - - /** Query the settlement history, ranked by time in descending order. */ - getOptionsSettlementHistory( - params: UMOptionsSettlementHistoryRequest, - ): Promise> { - return this.getPrivate('/unified/v3/private/delivery-record', params); - } - - /** Query session settlement records, only for USDC perpetual */ - getUSDCPerpetualSettlementHistory( - params: UMPerpSettlementHistoryRequest, - ): Promise> { - return this.getPrivate('/unified/v3/private/settlement-record', params); - } - - /** -> Account API */ - - /** Query wallet balance */ - getBalances(coin?: string): Promise> { - return this.getPrivate('/unified/v3/private/account/wallet/balance', { - coin, - }); - } - - /** - * Upgrade to unified margin account. - * WARNING: This is currently not reversable! - */ - upgradeToUnifiedMargin(): Promise> { - return this.postPrivate( - '/unified/v3/private/account/upgrade-unified-account', - ); - } - - /** Query trading history */ - getTransactionLog( - params: UMTransactionLogRequest, - ): Promise> { - return this.getPrivate( - '/unified/v3/private/account/transaction-log', - params, - ); - } - - /** Fund transfer between accounts (v2) */ - transferFunds(params: InternalTransferRequest): Promise> { - return this.postPrivate('/asset/v1/private/transfer', params); - } - - /** Exchange Coins */ - getCoinExchangeHistory( - params?: UMExchangeCoinsRequest, - ): Promise> { - return this.getPrivate( - '/asset/v2/private/exchange/exchange-order-all', - params, - ); - } - - /** Get Borrow History */ - getBorrowHistory( - params?: UMBorrowHistoryRequest, - ): Promise> { - return this.getPrivate( - '/unified/v3/private/account/borrow-history', - params, - ); - } - - /** Get Borrow Rate */ - getBorrowRate(currency?: string): Promise> { - return this.getPrivate('/unified/v3/private/account/borrow-rate', { - currency, - }); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } -} diff --git a/src/usdc-option-client.ts b/src/usdc-option-client.ts deleted file mode 100644 index 8db52e7..0000000 --- a/src/usdc-option-client.ts +++ /dev/null @@ -1,337 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - USDCOptionsActiveOrdersRealtimeRequest, - USDCOptionsActiveOrdersRequest, - USDCOptionsCancelAllOrdersRequest, - USDCOptionsCancelOrderRequest, - USDCOptionsContractInfoRequest, - USDCOptionsDeliveryHistoryRequest, - USDCOptionsDeliveryPriceRequest, - USDCOptionsHistoricOrdersRequest, - USDCOptionsHistoricalVolatilityRequest, - USDCOptionsModifyMMPRequest, - USDCOptionsModifyOrderRequest, - USDCOptionsOrderExecutionRequest, - USDCOptionsOrderRequest, - USDCOptionsPositionsInfoExpiryRequest, - USDCOptionsRecentTradesRequest, - USDCPositionsRequest, - USDCTransactionLogRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for USDC Option APIs - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class USDCOptionClient extends BaseRestClient { - getClientType() { - return REST_CLIENT_TYPE_ENUM.v3; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - /** Query order book info. Each side has a depth of 25 orders. */ - getOrderBook(symbol: string): Promise> { - return this.get('/option/usdc/openapi/public/v1/order-book', { symbol }); - } - - /** Fetch trading rules (such as min/max qty). Query for all if blank. */ - getContractInfo( - params?: USDCOptionsContractInfoRequest, - ): Promise> { - return this.get('/option/usdc/openapi/public/v1/symbols', params); - } - - /** Get a symbol price/statistics ticker */ - getSymbolTicker(symbol: string): Promise> { - return this.get('/option/usdc/openapi/public/v1/tick', { symbol }); - } - - /** Get delivery information */ - getDeliveryPrice( - params?: USDCOptionsDeliveryPriceRequest, - ): Promise> { - return this.get('/option/usdc/openapi/public/v1/delivery-price', params); - } - - /** Returned records are Taker Buy in default. */ - getLast500Trades( - params: USDCOptionsRecentTradesRequest, - ): Promise> { - return this.get( - '/option/usdc/openapi/public/v1/query-trade-latest', - params, - ); - } - - /** - * The data is in hourly. - * If time field is not passed, it returns the recent 1 hour data by default. - * It could be any timeframe by inputting startTime & endTime, but it must satisfy [endTime - startTime] <= 30 days. - * It returns all data in 2 years when startTime & endTime are not passed. - * Both startTime & endTime entered together or both are left blank - */ - getHistoricalVolatility( - params?: USDCOptionsHistoricalVolatilityRequest, - ): Promise> { - return this.get( - '/option/usdc/openapi/public/v1/query-historical-volatility', - params, - ); - } - - /** - * - * Account Data Endpoints - * - */ - - /** -> Order API */ - - /** - * Place an order using the USDC Derivatives Account. - * The request status can be queried in real-time. - * The response parameters must be queried through a query or a WebSocket response. - */ - submitOrder(params: USDCOptionsOrderRequest): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/place-order', - params, - ); - } - - /** - * Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request. - */ - batchSubmitOrders( - orderRequest: USDCOptionsOrderRequest[], - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/batch-place-orders', - { orderRequest }, - ); - } - - /** For Options, at least one of the three parameters — price, quantity or implied volatility — must be input. */ - modifyOrder( - params: USDCOptionsModifyOrderRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/replace-order', - params, - ); - } - - /** Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request. */ - batchModifyOrders( - replaceOrderRequest: USDCOptionsModifyOrderRequest[], - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/batch-replace-orders', - { replaceOrderRequest }, - ); - } - - /** Cancel order */ - cancelOrder( - params: USDCOptionsCancelOrderRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/cancel-order', - params, - ); - } - - /** Batch cancel orders */ - batchCancelOrders( - cancelRequest: USDCOptionsCancelOrderRequest[], - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/batch-cancel-orders', - { cancelRequest }, - ); - } - - /** This is used to cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */ - cancelActiveOrders( - params?: USDCOptionsCancelAllOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/cancel-all', - params, - ); - } - - /** Query Unfilled/Partially Filled Orders(real-time), up to last 7 days of partially filled/unfilled orders */ - getActiveRealtimeOrders( - params?: USDCOptionsActiveOrdersRealtimeRequest, - ): Promise> { - return this.getPrivate( - '/option/usdc/openapi/private/v1/trade/query-active-orders', - params, - ); - } - - /** Query Unfilled/Partially Filled Orders */ - getActiveOrders( - params: USDCOptionsActiveOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-active-orders', - params, - ); - } - - /** Query order history. The endpoint only supports up to 30 days of queried records */ - getHistoricOrders( - params: USDCOptionsHistoricOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-order-history', - params, - ); - } - - /** - * Query trade history. - * The endpoint only supports up to 30 days of queried records. - * An error will be returned if startTime is more than 30 days. - */ - getOrderExecutionHistory( - params: USDCOptionsOrderExecutionRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/execution-list', - params, - ); - } - - /** -> Account API */ - - /** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */ - getTransactionLog( - params: USDCTransactionLogRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-transaction-log', - params, - ); - } - - /** Wallet info for USDC account. */ - getBalances(): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-wallet-balance', - ); - } - - /** Asset Info */ - getAssetInfo(baseCoin?: string): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-asset-info', - { baseCoin }, - ); - } - - /** - * If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN, - * and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN. - * X and Y will be adjusted according to operational requirements. - * Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode. - */ - setMarginMode( - newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN', - ): Promise> { - return this.postPrivate( - '/option/usdc/private/asset/account/setMarginMode', - { setMarginMode: newMarginMode }, - ); - } - - /** Query margin mode for USDC account. */ - getMarginMode(): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-margin-info', - ); - } - - /** -> Positions API */ - - /** Query my positions */ - getPositions(params: USDCPositionsRequest): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-position', - params, - ); - } - - /** Query Delivery History */ - getDeliveryHistory( - params: USDCOptionsDeliveryHistoryRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-delivery-list', - params, - ); - } - - /** Query Positions Info Upon Expiry */ - getPositionsInfoUponExpiry( - params?: USDCOptionsPositionsInfoExpiryRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-position-exp-date', - params, - ); - } - - /** -> Market Maker Protection */ - - /** modifyMMP */ - modifyMMP(params: USDCOptionsModifyMMPRequest): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/mmp-modify', - params, - ); - } - - /** resetMMP */ - resetMMP(currency: string): Promise> { - return this.postPrivate('/option/usdc/openapi/private/v1/mmp-reset', { - currency, - }); - } - - /** queryMMPState */ - queryMMPState(baseCoin: string): Promise> { - return this.postPrivate('/option/usdc/openapi/private/v1/get-mmp-state', { - baseCoin, - }); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } -} diff --git a/src/usdc-perpetual-client.ts b/src/usdc-perpetual-client.ts deleted file mode 100644 index 0c76819..0000000 --- a/src/usdc-perpetual-client.ts +++ /dev/null @@ -1,320 +0,0 @@ -/* eslint-disable max-len */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - APIResponseV3, - APIResponseWithTime, - SymbolLimitParam, - SymbolPeriodLimitParam, - USDCKlineRequest, - USDCLast500TradesRequest, - USDCOpenInterestRequest, - USDCOrderFilter, - USDCPerpActiveOrdersRequest, - USDCPerpCancelOrderRequest, - USDCPerpHistoricOrdersRequest, - USDCPerpModifyOrderRequest, - USDCPerpOrderRequest, - USDCPositionsRequest, - USDCSymbolDirectionLimit, - USDCSymbolDirectionLimitCursor, - USDCTransactionLogRequest, -} from './types'; -import { REST_CLIENT_TYPE_ENUM } from './util'; -import BaseRestClient from './util/BaseRestClient'; - -/** - * REST API client for USDC Perpetual APIs - * - * @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet - * will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability. - * Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP. - */ -export class USDCPerpetualClient extends BaseRestClient { - getClientType() { - // Follows the same authentication mechanism as other v3 APIs (e.g. USDC) - return REST_CLIENT_TYPE_ENUM.v3; - } - - async fetchServerTime(): Promise { - const res = await this.getServerTime(); - return Number(res.time_now); - } - - /** - * - * Market Data Endpoints - * - */ - - getOrderBook(symbol: string): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/order-book', { symbol }); - } - - /** Fetch trading rules (such as min/max qty). Query for all if blank. */ - getContractInfo( - params?: USDCSymbolDirectionLimit, - ): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/symbols', params); - } - - /** Get a symbol price/statistics ticker */ - getSymbolTicker(symbol: string): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/tick', { symbol }); - } - - getCandles(params: USDCKlineRequest): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/kline/list', params); - } - - getMarkPrice(params: USDCKlineRequest): Promise> { - return this.get( - '/perpetual/usdc/openapi/public/v1/mark-price-kline', - params, - ); - } - - getIndexPrice(params: USDCKlineRequest): Promise> { - return this.get( - '/perpetual/usdc/openapi/public/v1/index-price-kline', - params, - ); - } - - getIndexPremium(params: USDCKlineRequest): Promise> { - return this.get( - '/perpetual/usdc/openapi/public/v1/premium-index-kline', - params, - ); - } - - getOpenInterest( - params: USDCOpenInterestRequest, - ): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/open-interest', params); - } - - getLargeOrders( - params: SymbolLimitParam, - ): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/big-deal', params); - } - - getLongShortRatio( - params: SymbolPeriodLimitParam, - ): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/account-ratio', params); - } - - getLast500Trades( - params: USDCLast500TradesRequest, - ): Promise> { - return this.get( - '/option/usdc/openapi/public/v1/query-trade-latest', - params, - ); - } - - /** - * - * Account Data Endpoints - * - */ - - /** -> Order API */ - - /** - * Place an order using the USDC Derivatives Account. - * The request status can be queried in real-time. - * The response parameters must be queried through a query or a WebSocket response. - */ - submitOrder(params: USDCPerpOrderRequest): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/place-order', - params, - ); - } - - /** - * 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: USDCPerpModifyOrderRequest): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/replace-order', - params, - ); - } - - /** Cancel order */ - cancelOrder(params: USDCPerpCancelOrderRequest): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/cancel-order', - params, - ); - } - - /** Cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */ - cancelActiveOrders( - symbol: string, - orderFilter: USDCOrderFilter, - ): Promise> { - return this.postPrivate('/perpetual/usdc/openapi/private/v1/cancel-all', { - symbol, - orderFilter, - }); - } - - /** Query Unfilled/Partially Filled Orders */ - getActiveOrders( - params: USDCPerpActiveOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-active-orders', - params, - ); - } - - /** Query order history. The endpoint only supports up to 30 days of queried records */ - getHistoricOrders( - params: USDCPerpHistoricOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-order-history', - params, - ); - } - - /** Query trade history. The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */ - getOrderExecutionHistory( - params: USDCPerpActiveOrdersRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/execution-list', - params, - ); - } - - /** -> Account API */ - - /** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */ - getTransactionLog( - params: USDCTransactionLogRequest, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-transaction-log', - params, - ); - } - - /** Wallet info for USDC account. */ - getBalances(): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-wallet-balance', - ); - } - - /** Asset Info */ - getAssetInfo(baseCoin?: string): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-asset-info', - { baseCoin }, - ); - } - - /** - * If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN, - * and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN. - * X and Y will be adjusted according to operational requirements. - * Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode. - */ - setMarginMode( - newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN', - ): Promise> { - return this.postPrivate( - '/option/usdc/private/asset/account/setMarginMode', - { setMarginMode: newMarginMode }, - ); - } - - /** Query margin mode for USDC account. */ - getMarginMode(): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-margin-info', - ); - } - - /** -> Positions API */ - - /** Query my positions */ - getPositions(params: USDCPositionsRequest): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/query-position', - params, - ); - } - - /** Only for REGULAR_MARGIN */ - setLeverage(symbol: string, leverage: string): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/position/leverage/save', - { symbol, leverage }, - ); - } - - /** Query Settlement History */ - getSettlementHistory( - params?: USDCSymbolDirectionLimitCursor, - ): Promise> { - return this.postPrivate( - '/option/usdc/openapi/private/v1/session-settlement', - params, - ); - } - - /** -> Risk Limit API */ - - /** Query risk limit */ - getRiskLimit(symbol: string): Promise> { - return this.getPrivate( - '/perpetual/usdc/openapi/public/v1/risk-limit/list', - { - symbol, - }, - ); - } - - /** Set risk limit */ - setRiskLimit(symbol: string, riskId: number): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/position/set-risk-limit', - { symbol, riskId }, - ); - } - - /** -> Funding API */ - - /** Funding settlement occurs every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC. The current interval's fund fee settlement is based on the previous interval's fund rate. For example, at 16:00, the settlement is based on the fund rate generated at 8:00. The fund rate generated at 16:00 will be used at 0:00 the next day. */ - getLastFundingRate(symbol: string): Promise> { - return this.get('/perpetual/usdc/openapi/public/v1/prev-funding-rate', { - symbol, - }); - } - - /** Get predicted funding rate and my predicted funding fee */ - getPredictedFundingRate(symbol: string): Promise> { - return this.postPrivate( - '/perpetual/usdc/openapi/private/v1/predicted-funding', - { symbol }, - ); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): Promise { - return this.get('/v2/public/time'); - } -} diff --git a/src/util/websockets/websocket-util.ts b/src/util/websockets/websocket-util.ts index 1454a31..883d14c 100644 --- a/src/util/websockets/websocket-util.ts +++ b/src/util/websockets/websocket-util.ts @@ -13,25 +13,6 @@ import { WSAPIRequest } from '../../types/websockets/ws-api'; export const WS_LOGGER_CATEGORY = { category: 'bybit-ws' }; export const WS_KEY_MAP = { - inverse: 'inverse', - linearPrivate: 'linearPrivate', - linearPublic: 'linearPublic', - spotPrivate: 'spotPrivate', - spotPublic: 'spotPublic', - spotV3Private: 'spotV3Private', - spotV3Public: 'spotV3Public', - usdcOptionPrivate: 'usdcOptionPrivate', - usdcOptionPublic: 'usdcOptionPublic', - usdcPerpPrivate: 'usdcPerpPrivate', - usdcPerpPublic: 'usdcPerpPublic', - unifiedPrivate: 'unifiedPrivate', - unifiedOptionPublic: 'unifiedOptionPublic', - unifiedPerpUSDTPublic: 'unifiedPerpUSDTPublic', - unifiedPerpUSDCPublic: 'unifiedPerpUSDCPublic', - contractUSDTPublic: 'contractUSDTPublic', - contractUSDTPrivate: 'contractUSDTPrivate', - contractInversePublic: 'contractInversePublic', - contractInversePrivate: 'contractInversePrivate', v5SpotPublic: 'v5SpotPublic', v5LinearPublic: 'v5LinearPublic', v5InversePublic: 'v5InversePublic', @@ -44,27 +25,11 @@ export const WS_KEY_MAP = { } as const; export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [ - WS_KEY_MAP.spotV3Private, - WS_KEY_MAP.usdcOptionPrivate, - WS_KEY_MAP.usdcPerpPrivate, - WS_KEY_MAP.unifiedPrivate, - WS_KEY_MAP.contractUSDTPrivate, - WS_KEY_MAP.contractInversePrivate, WS_KEY_MAP.v5Private, WS_KEY_MAP.v5PrivateTrade, ]; export const PUBLIC_WS_KEYS = [ - WS_KEY_MAP.linearPublic, - WS_KEY_MAP.spotPublic, - WS_KEY_MAP.spotV3Public, - WS_KEY_MAP.usdcOptionPublic, - WS_KEY_MAP.usdcPerpPublic, - WS_KEY_MAP.unifiedOptionPublic, - WS_KEY_MAP.unifiedPerpUSDTPublic, - WS_KEY_MAP.unifiedPerpUSDCPublic, - WS_KEY_MAP.contractUSDTPublic, - WS_KEY_MAP.contractInversePublic, WS_KEY_MAP.v5SpotPublic, WS_KEY_MAP.v5LinearPublic, WS_KEY_MAP.v5InversePublic, @@ -157,8 +122,6 @@ type PublicPrivateNetwork = 'public' | 'private'; * For the v5 endpoints, the subscribe/unsubscribe call must specify the category the subscription should route to. */ type PublicOnlyWsKeys = - | 'unifiedPerpUSDT' - | 'unifiedPerpUSDC' | 'v5SpotPublic' | 'v5LinearPublic' | 'v5InversePublic' @@ -217,126 +180,6 @@ export const WS_BASE_URL_MAP: Record< testnet: 'wss://stream-testnet.bybit.com/v5/public/option', }, }, - inverse: { - public: { - livenet: 'wss://stream.bybit.com/realtime', - testnet: 'wss://stream-testnet.bybit.com/realtime', - }, - private: { - livenet: 'wss://stream.bybit.com/realtime', - testnet: 'wss://stream-testnet.bybit.com/realtime', - }, - }, - linear: { - public: { - livenet: 'wss://stream.bybit.com/realtime_public', - livenet2: 'wss://stream.bytick.com/realtime_public', - testnet: 'wss://stream-testnet.bybit.com/realtime_public', - }, - private: { - livenet: 'wss://stream.bybit.com/realtime_private', - livenet2: 'wss://stream.bytick.com/realtime_private', - testnet: 'wss://stream-testnet.bybit.com/realtime_private', - }, - }, - spot: { - public: { - livenet: 'wss://stream.bybit.com/spot/quote/ws/v1', - livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2', - testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1', - testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2', - }, - private: { - livenet: 'wss://stream.bybit.com/spot/ws', - testnet: 'wss://stream-testnet.bybit.com/spot/ws', - }, - }, - spotv3: { - public: { - livenet: 'wss://stream.bybit.com/spot/public/v3', - testnet: 'wss://stream-testnet.bybit.com/spot/public/v3', - }, - private: { - livenet: 'wss://stream.bybit.com/spot/private/v3', - testnet: 'wss://stream-testnet.bybit.com/spot/private/v3', - }, - }, - usdcOption: { - public: { - livenet: 'wss://stream.bybit.com/trade/option/usdc/public/v1', - livenet2: 'wss://stream.bytick.com/trade/option/usdc/public/v1', - testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/public/v1', - }, - private: { - livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1', - livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1', - testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1', - }, - }, - usdcPerp: { - public: { - livenet: 'wss://stream.bybit.com/perpetual/ws/v1/realtime_public', - livenet2: 'wss://stream.bytick.com/perpetual/ws/v1/realtime_public', - testnet: 'wss://stream-testnet.bybit.com/perpetual/ws/v1/realtime_public', - }, - private: { - livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1', - livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1', - testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1', - }, - }, - unifiedOption: { - public: { - livenet: 'wss://stream.bybit.com/option/usdc/public/v3', - testnet: 'wss://stream-testnet.bybit.com/option/usdc/public/v3', - }, - private: { - livenet: 'wss://stream.bybit.com/unified/private/v3', - testnet: 'wss://stream-testnet.bybit.com/unified/private/v3', - }, - }, - unifiedPerp: { - public: { - livenet: 'useBaseSpecificEndpoint', - testnet: 'useBaseSpecificEndpoint', - }, - private: { - livenet: 'wss://stream.bybit.com/unified/private/v3', - testnet: 'wss://stream-testnet.bybit.com/unified/private/v3', - }, - }, - unifiedPerpUSDT: { - public: { - livenet: 'wss://stream.bybit.com/contract/usdt/public/v3', - testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3', - }, - }, - unifiedPerpUSDC: { - public: { - livenet: 'wss://stream.bybit.com/contract/usdc/public/v3', - testnet: 'wss://stream-testnet.bybit.com/contract/usdc/public/v3', - }, - }, - 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 function isPrivateWsTopic(topic: string): boolean { @@ -351,68 +194,6 @@ export function getWsKeyForTopic( ): WsKey { const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic); switch (market) { - case 'inverse': { - return WS_KEY_MAP.inverse; - } - case 'linear': { - return isPrivateTopic - ? WS_KEY_MAP.linearPrivate - : WS_KEY_MAP.linearPublic; - } - case 'spot': { - return isPrivateTopic ? WS_KEY_MAP.spotPrivate : WS_KEY_MAP.spotPublic; - } - case 'spotv3': { - return isPrivateTopic - ? WS_KEY_MAP.spotV3Private - : WS_KEY_MAP.spotV3Public; - } - case 'usdcOption': { - return isPrivateTopic - ? WS_KEY_MAP.usdcOptionPrivate - : WS_KEY_MAP.usdcOptionPublic; - } - case 'usdcPerp': { - return isPrivateTopic - ? WS_KEY_MAP.usdcPerpPrivate - : WS_KEY_MAP.usdcPerpPublic; - } - case 'unifiedOption': { - return isPrivateTopic - ? WS_KEY_MAP.unifiedPrivate - : WS_KEY_MAP.unifiedOptionPublic; - } - case 'unifiedPerp': { - if (isPrivateTopic) { - return WS_KEY_MAP.unifiedPrivate; - } - - const upperTopic = topic.toUpperCase(); - if (upperTopic.indexOf('USDT') !== -1) { - return WS_KEY_MAP.unifiedPerpUSDTPublic; - } - - if ( - upperTopic.indexOf('USDC') !== -1 || - upperTopic.indexOf('PERP') !== -1 - ) { - return WS_KEY_MAP.unifiedPerpUSDCPublic; - } - - throw new Error( - `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; - } case 'v5': { if (isPrivateTopic) { return WS_KEY_MAP.v5Private; @@ -486,64 +267,6 @@ export function getWsUrl( case WS_KEY_MAP.v5OptionPublic: { return WS_BASE_URL_MAP.v5OptionPublic.public[networkKey]; } - case WS_KEY_MAP.linearPublic: { - return WS_BASE_URL_MAP.linear.public[networkKey]; - } - case WS_KEY_MAP.linearPrivate: { - return WS_BASE_URL_MAP.linear.private[networkKey]; - } - case WS_KEY_MAP.spotPublic: { - return WS_BASE_URL_MAP.spot.public[networkKey]; - } - case WS_KEY_MAP.spotPrivate: { - return WS_BASE_URL_MAP.spot.private[networkKey]; - } - case WS_KEY_MAP.spotV3Public: { - return WS_BASE_URL_MAP.spotv3.public[networkKey]; - } - case WS_KEY_MAP.spotV3Private: { - return WS_BASE_URL_MAP.spotv3.private[networkKey]; - } - case WS_KEY_MAP.inverse: { - // private and public are on the same WS connection - return WS_BASE_URL_MAP.inverse.public[networkKey]; - } - case WS_KEY_MAP.usdcOptionPublic: { - return WS_BASE_URL_MAP.usdcOption.public[networkKey]; - } - case WS_KEY_MAP.usdcOptionPrivate: { - return WS_BASE_URL_MAP.usdcOption.private[networkKey]; - } - case WS_KEY_MAP.usdcPerpPublic: { - return WS_BASE_URL_MAP.usdcPerp.public[networkKey]; - } - case WS_KEY_MAP.usdcPerpPrivate: { - return WS_BASE_URL_MAP.usdcPerp.private[networkKey]; - } - case WS_KEY_MAP.unifiedOptionPublic: { - return WS_BASE_URL_MAP.unifiedOption.public[networkKey]; - } - case WS_KEY_MAP.unifiedPerpUSDTPublic: { - return WS_BASE_URL_MAP.unifiedPerpUSDT.public[networkKey]; - } - case WS_KEY_MAP.unifiedPerpUSDCPublic: { - return WS_BASE_URL_MAP.unifiedPerpUSDC.public[networkKey]; - } - 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: { logger.error('getWsUrl(): Unhandled wsKey: ', { category: 'bybit-ws', @@ -558,50 +281,19 @@ export function getMaxTopicsPerSubscribeEvent( market: APIMarket, wsKey: WsKey, ): number | null { - const topicsPerEventSpot = 10; switch (market) { - case 'inverse': - case 'linear': - case 'usdcOption': - case 'usdcPerp': - case 'unifiedOption': - case 'unifiedPerp': - case 'spot': - case 'contractInverse': - case 'contractUSDT': case 'v5': { if (wsKey === WS_KEY_MAP.v5SpotPublic) { - return topicsPerEventSpot; + return 10; } return null; } - case 'spotv3': { - return topicsPerEventSpot; - } default: { throw neverGuard(market, 'getWsKeyForTopic(): Unhandled market'); } } } -export function getUsdcWsKeyForTopic( - topic: string, - subGroup: 'option' | 'perp', -): WsKey { - const isPrivateTopic = PRIVATE_TOPICS.includes(topic); - if (subGroup === 'option') { - return isPrivateTopic - ? WS_KEY_MAP.usdcOptionPrivate - : WS_KEY_MAP.usdcOptionPublic; - } - return isPrivateTopic - ? WS_KEY_MAP.usdcOptionPrivate - : WS_KEY_MAP.usdcOptionPublic; - // return isPrivateTopic - // ? WS_KEY_MAP.usdcPerpPrivate - // : WS_KEY_MAP.usdcPerpPublic; -} - export const WS_ERROR_ENUM = { NOT_AUTHENTICATED_SPOT_V3: '-1004', API_ERROR_GENERIC: '10001', @@ -610,7 +302,7 @@ export const WS_ERROR_ENUM = { }; export function neverGuard(x: never, msg: string): Error { - return new Error(`Unhandled value exception "x", ${msg}`); + return new Error(`Unhandled value exception "${x}", ${msg}`); } /** @@ -670,6 +362,7 @@ export function getNormalisedTopicRequests( * @returns */ export function getTopicsPerWSKey( + market: APIMarket, normalisedTopicRequests: WsTopicRequest[], wsKey?: WsKey, isPrivateTopic?: boolean, @@ -683,7 +376,7 @@ export function getTopicsPerWSKey( const derivedWsKey = wsKey || getWsKeyForTopic( - this.options.market, + market, topicRequest.topic, isPrivateTopic, topicRequest.category, diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 07d1288..3ed2ccf 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -50,28 +50,13 @@ export class WebsocketClient extends BaseWebsocketClient< WsRequestOperationBybit > { /** - * Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library + * Request connection of all dependent (public & private) websockets, instead of waiting + * for automatic connection by SDK. */ public connectAll(): Promise[] { switch (this.options.market) { - case 'inverse': { - // only one for inverse - return [...this.connectPublic()]; - } - // these all have separate public & private ws endpoints - case 'linear': - case 'spot': - case 'spotv3': - case 'usdcOption': - case 'usdcPerp': - case 'unifiedPerp': - case 'unifiedOption': - case 'contractUSDT': - case 'contractInverse': { - return [...this.connectPublic(), this.connectPrivate()]; - } case 'v5': { - return [this.connectPrivate()]; + return [...this.connectPublic(), this.connectPrivate()]; } default: { throw neverGuard(this.options.market, 'connectAll(): Unhandled market'); @@ -101,37 +86,6 @@ export class WebsocketClient extends BaseWebsocketClient< this.connect(WS_KEY_MAP.v5OptionPublic), ]; } - case 'inverse': { - return [this.connect(WS_KEY_MAP.inverse)]; - } - case 'linear': { - return [this.connect(WS_KEY_MAP.linearPublic)]; - } - case 'spot': { - return [this.connect(WS_KEY_MAP.spotPublic)]; - } - case 'spotv3': { - return [this.connect(WS_KEY_MAP.spotV3Public)]; - } - case 'usdcOption': { - return [this.connect(WS_KEY_MAP.usdcOptionPublic)]; - } - case 'usdcPerp': { - return [this.connect(WS_KEY_MAP.usdcPerpPublic)]; - } - case 'unifiedOption': { - return [this.connect(WS_KEY_MAP.unifiedOptionPublic)]; - } - case 'unifiedPerp': { - return [ - this.connect(WS_KEY_MAP.unifiedPerpUSDTPublic), - this.connect(WS_KEY_MAP.unifiedPerpUSDCPublic), - ]; - } - case 'contractUSDT': - return [this.connect(WS_KEY_MAP.contractUSDTPublic)]; - case 'contractInverse': - return [this.connect(WS_KEY_MAP.contractInversePublic)]; } } @@ -141,40 +95,16 @@ export class WebsocketClient extends BaseWebsocketClient< default: { return this.connect(WS_KEY_MAP.v5Private); } - case 'inverse': { - return this.connect(WS_KEY_MAP.inverse); - } - case 'linear': { - return this.connect(WS_KEY_MAP.linearPrivate); - } - case 'spot': { - return this.connect(WS_KEY_MAP.spotPrivate); - } - case 'spotv3': { - return this.connect(WS_KEY_MAP.spotV3Private); - } - case 'usdcOption': { - return this.connect(WS_KEY_MAP.usdcOptionPrivate); - } - case 'usdcPerp': { - return this.connect(WS_KEY_MAP.usdcPerpPrivate); - } - case 'unifiedPerp': - 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); } } /** * Subscribe to V5 topics & track/persist them. * @param wsTopics - topic or list of topics - * @param category - the API category this topic is for (e.g. "linear"). The value is only important when connecting to public topics and will be ignored for private topics. - * @param isPrivateTopic - optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) + * @param category - the API category this topic is for (e.g. "linear"). + * The value is only important when connecting to public topics and will be ignored for private topics. + * @param isPrivateTopic - optional - the library will try to detect private topics, you can use this + * to mark a topic as private (if the topic isn't recognised yet) */ public subscribeV5( wsTopics: WsTopic[] | WsTopic, @@ -233,10 +163,14 @@ export class WebsocketClient extends BaseWebsocketClient< } /** - * Unsubscribe from V5 topics & remove them from memory. They won't be re-subscribed to if the connection reconnects. + * Unsubscribe from V5 topics & remove them from memory. They won't be re-subscribed to if the + * connection reconnects. + * * @param wsTopics - topic or list of topics - * @param category - the API category this topic is for (e.g. "linear"). The value is only important when connecting to public topics and will be ignored for private topics. - * @param isPrivateTopic - optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) + * @param category - the API category this topic is for (e.g. "linear"). The value is only + * important when connecting to public topics and will be ignored for private topics. + * @param isPrivateTopic - optional - the library will try to detect private topics, you can + * use this to mark a topic as private (if the topic isn't recognised yet) */ public unsubscribeV5( wsTopics: WsTopic[] | WsTopic, @@ -297,7 +231,9 @@ export class WebsocketClient extends BaseWebsocketClient< /** * Note: subscribeV5() might be simpler to use. The end result is the same. * - * Request subscription to one or more topics. Pass topics as either an array of strings, or array of objects (if the topic has parameters). + * Request subscription to one or more topics. Pass topics as either an array of strings, + * or array of objects (if the topic has parameters). + * * Objects should be formatted as {topic: string, params: object, category: CategoryV5}. * * - Subscriptions are automatically routed to the correct websocket connection. @@ -315,7 +251,11 @@ export class WebsocketClient extends BaseWebsocketClient< const topicRequests = Array.isArray(requests) ? requests : [requests]; const normalisedTopicRequests = getNormalisedTopicRequests(topicRequests); - const perWsKeyTopics = getTopicsPerWSKey(normalisedTopicRequests, wsKey); + const perWsKeyTopics = getTopicsPerWSKey( + this.options.market, + normalisedTopicRequests, + wsKey, + ); // Batch sub topics per ws key for (const wsKey in perWsKeyTopics) { @@ -342,7 +282,11 @@ export class WebsocketClient extends BaseWebsocketClient< const topicRequests = Array.isArray(requests) ? requests : [requests]; const normalisedTopicRequests = getNormalisedTopicRequests(topicRequests); - const perWsKeyTopics = getTopicsPerWSKey(normalisedTopicRequests, wsKey); + const perWsKeyTopics = getTopicsPerWSKey( + this.options.market, + normalisedTopicRequests, + wsKey, + ); // Batch sub topics per ws key for (const wsKey in perWsKeyTopics) { @@ -374,11 +318,14 @@ export class WebsocketClient extends BaseWebsocketClient< * - an exception is detected in the reply, OR * - the connection disconnects for any reason (even if automatic reconnect will happen). * - * Authentication is automatic. If you didn't request authentication yourself, there might be a small delay after your first request, while the SDK automatically authenticates. + * Authentication is automatic. If you didn't request authentication yourself, there might + * be a small delay after your first request, while the SDK automatically authenticates. * - * @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported for Bybit, since that is the dedicated WS API connection. + * @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported + * for Bybit, since that is the dedicated WS API connection. * @param operation - The command being sent, e.g. "order.create" to submit a new order. - * @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new order. Only send parameters for the request body. Everything else is automatically handled. + * @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new + * order. Only send parameters for the request body. Everything else is automatically handled. * @returns Promise - tries to resolve with async WS API response. Rejects if disconnected or exception is seen in async WS API response */ @@ -393,8 +340,10 @@ export class WebsocketClient extends BaseWebsocketClient< ...params: TWSParams extends undefined ? [] : [TWSParams] ): Promise; - // These overloads give stricter types than mapped generics, since generic constraints do not trigger excess property checks - // Without these overloads, TypeScript won't complain if you include an unexpected property with your request (if it doesn't clash with an existing property) + // These overloads give stricter types than mapped generics, since generic constraints + // do not trigger excess property checks + // Without these overloads, TypeScript won't complain if you include an + // unexpected property with your request (if it doesn't clash with an existing property) sendWSAPIRequest( wsKey: typeof WS_KEY_MAP.v5PrivateTrade, operation: TWSOpreation, @@ -483,7 +432,7 @@ export class WebsocketClient extends BaseWebsocketClient< const wsBaseURL = getWsUrl(wsKey, this.options, this.logger); // If auth is needed for this wsKey URL, this returns a suffix - const authParams = await this.getWsAuthURLSuffix(wsKey); + const authParams = await this.getWsAuthURLSuffix(); if (!authParams) { return wsBaseURL; } @@ -494,8 +443,7 @@ export class WebsocketClient extends BaseWebsocketClient< /** * Return params required to make authorized request */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars - private async getWsAuthURLSuffix(wsKey: WsKey): Promise { + private async getWsAuthURLSuffix(): Promise { return ''; } @@ -581,7 +529,6 @@ export class WebsocketClient extends BaseWebsocketClient< } /** Force subscription requests to be sent in smaller batches, if a number is returned */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars protected getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null { return getMaxTopicsPerSubscribeEvent(this.options.market, wsKey); } @@ -648,17 +595,7 @@ export class WebsocketClient extends BaseWebsocketClient< } protected getPrivateWSKeys(): WsKey[] { - return [ - WS_KEY_MAP.linearPrivate, - WS_KEY_MAP.spotPrivate, - WS_KEY_MAP.spotV3Private, - WS_KEY_MAP.usdcOptionPrivate, - WS_KEY_MAP.usdcPerpPrivate, - WS_KEY_MAP.unifiedPrivate, - WS_KEY_MAP.contractUSDTPrivate, - WS_KEY_MAP.contractInversePrivate, - WS_KEY_MAP.v5Private, - ]; + return WS_AUTH_ON_CONNECT_KEYS; } protected isAuthOnConnectWsKey(wsKey: WsKey): boolean { @@ -668,11 +605,7 @@ export class WebsocketClient extends BaseWebsocketClient< /** * Determines if a topic is for a private channel, using a hardcoded list of strings */ - protected isPrivateTopicRequest( - request: WsTopicRequest, - // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars - wsKey: WsKey, - ): boolean { + protected isPrivateTopicRequest(request: WsTopicRequest): boolean { const topicName = request?.topic?.toLowerCase(); if (!topicName) { return false; From 03169b324ce4e051981ede452034222b89c91135 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 15:28:28 +0000 Subject: [PATCH 04/23] chore(): deprecate pre-V5 authentication workflows --- src/rest-client-v5.ts | 2 +- src/spot-client-v3.ts | 4 +- src/util/BaseRestClient.ts | 62 +++++++-------------------- src/util/requestUtils.ts | 28 +----------- src/util/typeGuards.ts | 28 ++++++++++++ src/util/webCryptoAPI.ts | 2 +- src/util/websockets/websocket-util.ts | 5 +-- 7 files changed, 50 insertions(+), 81 deletions(-) diff --git a/src/rest-client-v5.ts b/src/rest-client-v5.ts index 0262485..0913f3e 100644 --- a/src/rest-client-v5.ts +++ b/src/rest-client-v5.ts @@ -272,7 +272,7 @@ export class RestClientV5 extends BaseRestClient { */ getClientType() { - return REST_CLIENT_TYPE_ENUM.v3; + return REST_CLIENT_TYPE_ENUM.v5; } async fetchServerTime(): Promise { diff --git a/src/spot-client-v3.ts b/src/spot-client-v3.ts index 8c8d0f4..0ccf887 100644 --- a/src/spot-client-v3.ts +++ b/src/spot-client-v3.ts @@ -12,8 +12,8 @@ import BaseRestClient from './util/BaseRestClient'; */ export class SpotClientV3 extends BaseRestClient { getClientType() { - // Follows the same authentication mechanism as other v3 APIs (e.g. USDC) - return REST_CLIENT_TYPE_ENUM.v3; + // Doesn't really matter here, since the only remaining endpoint does not require auth. + return REST_CLIENT_TYPE_ENUM.v5; } async fetchServerTime(): Promise { diff --git a/src/util/BaseRestClient.ts b/src/util/BaseRestClient.ts index 5b43ae2..2309ec9 100644 --- a/src/util/BaseRestClient.ts +++ b/src/util/BaseRestClient.ts @@ -164,10 +164,6 @@ export default abstract class BaseRestClient { } } - private isSpotV1Client() { - return this.clientType === REST_CLIENT_TYPE_ENUM.spot; - } - get(endpoint: string, params?: any) { return this._call('GET', endpoint, params, true); } @@ -263,56 +259,34 @@ export default abstract class BaseRestClient { }; } - // USDC endpoints, unified margin and a few others use a different way of authenticating requests (headers instead of params) - if (this.clientType === REST_CLIENT_TYPE_ENUM.v3) { - const signResult = await this.prepareSignParams( - method, - 'v5auth', - params, - isPublicApi, - ); - - const headers: AxiosRequestConfig['headers'] = { - 'X-BAPI-SIGN-TYPE': 2, - 'X-BAPI-API-KEY': this.key, - 'X-BAPI-TIMESTAMP': signResult.timestamp, - 'X-BAPI-SIGN': signResult.sign, - 'X-BAPI-RECV-WINDOW': signResult.recvWindow, - ...options.headers, - }; - - if (method === 'GET') { - return { - ...options, - headers, - params: signResult.originalParams, - }; - } - - return { - ...options, - headers, - data: signResult.originalParams, - }; - } - const signResult = await this.prepareSignParams( method, - 'v2auth', + 'v5auth', params, isPublicApi, ); - if (method === 'GET' || this.isSpotV1Client()) { + const headers: AxiosRequestConfig['headers'] = { + 'X-BAPI-SIGN-TYPE': 2, + 'X-BAPI-API-KEY': this.key, + 'X-BAPI-TIMESTAMP': signResult.timestamp, + 'X-BAPI-SIGN': signResult.sign, + 'X-BAPI-RECV-WINDOW': signResult.recvWindow, + ...options.headers, + }; + + if (method === 'GET') { return { ...options, - params: signResult.paramsWithSign, + headers, + params: signResult.originalParams, }; } return { ...options, - data: signResult.paramsWithSign, + headers, + data: signResult.originalParams, }; } @@ -476,11 +450,7 @@ export default abstract class BaseRestClient { // Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen. if (recvWindow) { - if (this.isSpotV1Client()) { - res.originalParams.recvWindow = recvWindow; - } else { - res.originalParams.recv_window = recvWindow; - } + res.originalParams.recv_window = recvWindow; } const sortProperties = true; const encodeValues = false; diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 492f7a5..751482b 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -207,39 +207,13 @@ export function isTopicSubscriptionConfirmation( return true; } -export function isWSAPIResponse( - msg: unknown, -): msg is Omit { - if (typeof msg !== 'object' || !msg) { - return false; - } - - if (typeof msg['op'] !== 'string') { - return false; - } - - return (WS_API_Operations as string[]).includes(msg['op']); -} - -export function isTopicSubscriptionSuccess( - msg: unknown, -): msg is WebsocketSucceededTopicSubscriptionConfirmationEvent { - if (!isTopicSubscriptionConfirmation(msg)) return false; - return msg.success === true; -} - export const APIID = 'bybitapinode'; /** * Used to switch how authentication/requests work under the hood (primarily for SPOT since it's different there) */ export const REST_CLIENT_TYPE_ENUM = { - accountAsset: 'accountAsset', - inverse: 'inverse', - inverseFutures: 'inverseFutures', - linear: 'linear', - spot: 'spot', - v3: 'v3', + v5: 'v5', } as const; export type RestClientType = diff --git a/src/util/typeGuards.ts b/src/util/typeGuards.ts index b391930..f4bcc67 100644 --- a/src/util/typeGuards.ts +++ b/src/util/typeGuards.ts @@ -2,12 +2,15 @@ * Use type guards to narrow down types with minimal efforts. */ +import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types'; +import { WSAPIResponse, WS_API_Operations } from '../types/websockets/ws-api'; import { WSAccountOrderEventV5, WSExecutionEventV5, WSOrderbookEventV5, WSPositionEventV5, } from '../types/websockets/ws-events'; +import { isTopicSubscriptionConfirmation } from './requestUtils'; /** * Type guard to detect a V5 orderbook event (delta & snapshots) @@ -92,3 +95,28 @@ export function isWsExecutionEventV5( return event['topic'] === 'execution'; } + +export function neverGuard(x: never, msg: string): Error { + return new Error(`Unhandled value exception "${x}", ${msg}`); +} + +export function isWSAPIResponse( + msg: unknown, +): msg is Omit { + if (typeof msg !== 'object' || !msg) { + return false; + } + + if (typeof msg['op'] !== 'string') { + return false; + } + + return (WS_API_Operations as string[]).includes(msg['op']); +} + +export function isTopicSubscriptionSuccess( + msg: unknown, +): msg is WebsocketSucceededTopicSubscriptionConfirmationEvent { + if (!isTopicSubscriptionConfirmation(msg)) return false; + return msg.success === true; +} diff --git a/src/util/webCryptoAPI.ts b/src/util/webCryptoAPI.ts index 2aea730..2743aac 100644 --- a/src/util/webCryptoAPI.ts +++ b/src/util/webCryptoAPI.ts @@ -1,4 +1,4 @@ -import { neverGuard } from './websockets'; +import { neverGuard } from './typeGuards'; function bufferToB64(buffer: ArrayBuffer): string { let binary = ''; diff --git a/src/util/websockets/websocket-util.ts b/src/util/websockets/websocket-util.ts index 883d14c..51c4b20 100644 --- a/src/util/websockets/websocket-util.ts +++ b/src/util/websockets/websocket-util.ts @@ -9,6 +9,7 @@ import { import { DefaultLogger } from '../logger'; import { WSAPIRequest } from '../../types/websockets/ws-api'; +import { neverGuard } from '../typeGuards'; export const WS_LOGGER_CATEGORY = { category: 'bybit-ws' }; @@ -301,10 +302,6 @@ export const WS_ERROR_ENUM = { USDC_OPTION_AUTH_FAILED: '3303006', }; -export function neverGuard(x: never, msg: string): Error { - return new Error(`Unhandled value exception "${x}", ${msg}`); -} - /** * #305: ws.terminate() is undefined in browsers. * This only works in node.js, not in browsers. From a4945d1cafe5ec8ca6f613ec7dc1a57621e3c9e4 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 15:35:11 +0000 Subject: [PATCH 05/23] chore(): updates for pre-V5 deprecations --- README.md | 74 ++------------------------------------ src/util/BaseRestClient.ts | 38 +------------------- src/util/requestUtils.ts | 24 ------------- src/util/typeGuards.ts | 25 +++++++++++-- 4 files changed, 27 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 598433c..52c7927 100644 --- a/README.md +++ b/README.md @@ -57,15 +57,14 @@ Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs and WebSockets: ## Structure & Usage - [Structure](#structure) - [API Clients](#api-clients) -- [Usage](#usage) +- [REST API USAGE](#rest-api-usage) ## WebSocket Integration - [WebSockets](#websockets) - [WebSocket Subscriptions - Consuming Events](#websocket-subscriptions---consuming-events) - [Websocket API - Sending Orders via WebSockets](#websocket-api---sending-orders-via-websockets) - [Specifying Other Markets](#specifying-other-markets) -- [Load Balancing](#balancing-load-across-multiple-connections) -- [Older Websocket APIs](#older-websocket-apis) +- [Consumer Load Balancing](#balancing-load-across-multiple-connections) ## Additional Features - [Logging](#logging) @@ -154,7 +153,7 @@ Here are the available REST clients and the corresponding API groups described i | [WebsocketClient](src/websocket-client.ts) | All WebSocket Events (Public & Private for all API categories) | -### Usage +## REST API Usage Create API credentials on Bybit's website: @@ -257,38 +256,6 @@ client.getOrderbook({ category: 'linear', symbol: 'BTCUSDT' }) --- -### Deprecated/Obsolete REST APIs - -The following API clients are for previous generation REST APIs and will be removed in the next major release. Some have already stopped working (because bybit stopped supporting them). You should use the V5 APIs for all new development. - -
- Click me to read more - -Each generation is labelled with the version number (e.g. v1/v2/v3/v5). New projects & developments should use the newest available API generation (e.g. use the V5 APIs instead of V3). - - -| Class | Description | -| :--------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------: | -| [ **Derivatives v3** ] | The Derivatives v3 APIs (successor to the Futures V2 APIs) | -| [UnifiedMarginClient](src/unified-margin-client.ts) |[Derivatives (v3) Unified Margin APIs](https://bybit-exchange.github.io/docs/derivatives/unified/place-order) | -| [ContractClient](src/contract-client.ts) | [Derivatives (v3) Contract APIs](https://bybit-exchange.github.io/docs/derivatives/contract/place-order). | -| [ **Futures v2** ] | The Futures v2 APIs | -| [~~InverseClient~~](src/inverse-client.ts) | [Inverse Perpetual Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/inverse/) | -| [~~LinearClient~~](src/linear-client.ts) | [USDT Perpetual Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/linear/#t-introduction) | -| [~~InverseFuturesClient~~](src/inverse-futures-client.ts) | [Inverse Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/inverse_futures/#t-introduction) | -| [ **Spot** ] | The spot APIs | -| [SpotClientV3](src/spot-client-v3.ts) | [Spot Market (v3) APIs](https://bybit-exchange.github.io/docs/spot/public/instrument) | -| [~~SpotClient~~](src/spot-client.ts) (deprecated, SpotClientV3 recommended) | [Spot Market (v1) APIs](https://bybit-exchange.github.io/docs/spot/v1/#t-introduction) | -| [ **USDC Contract** ] | The USDC Contract APIs | -| [USDCPerpetualClient](src/usdc-perpetual-client.ts) | [USDC Perpetual APIs](https://bybit-exchange.github.io/docs/usdc/option/?console#t-querydeliverylog) | -| [USDCOptionClient](src/usdc-option-client.ts) | [USDC Option APIs](https://bybit-exchange.github.io/docs/usdc/option/#t-introduction) | -| [~~AccountAssetClient~~](src/account-asset-client.ts) | [Account Asset V1 APIs](https://bybit-exchange.github.io/docs/account_asset/v1/#t-introduction) | -| [ **Other** ] | Other standalone API groups | -| [CopyTradingClient](src/copy-trading-client.ts) | [Copy Trading APIs](https://bybit-exchange.github.io/docs/category/copy-trade) | -| [AccountAssetClientV3](src/account-asset-client-v3.ts) | [Account Asset V3 APIs](https://bybit-exchange.github.io/docs/account-asset/internal-transfer) | - -
- ## WebSockets The WebsocketClient will automatically use the latest V5 WebSocket endpoints by default. To use a different endpoint, use the `market` parameter. Except for the WebSocket API - this can be accessed without any special configuration. @@ -317,15 +284,6 @@ const wsConfig = { The following parameters are optional: */ - /** - * The API group this client should connect to. The V5 market is currently used by default. - * - * For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account - * asset/copy trading). - * Note that older API groups are deprecated and may stop working soon. - */ - // market: 'v5', - /** * Set to `true` to connect to Bybit's testnet environment. * - If demo trading, `testnet` should be set to false! @@ -553,32 +511,6 @@ Important: do not subscribe to the same topics on both clients or you will recei --- -### Older Websocket APIs - -The following API groups are still available in the WebsocketClient but are deprecated and may no longer work. They will be removed in the next major release: - -
- Click me to see the table - -| API Category | Market | Description | -| :------------------------------: | :-------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ~~Unified Margin - Options~~ | `market: 'unifiedOption'` | The [derivatives v3](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin/#t-websocket) category for unified margin. Note: public topics only support options topics. If you need USDC/USDT perps, use `unifiedPerp` instead. | -| ~~Unified Margin - Perps~~ | `market: 'unifiedPerp'` | The [derivatives v3](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin/#t-websocket) category for unified margin. Note: public topics only support USDT/USDC perpetual topics - use `unifiedOption` if you need public options topics. | -| ~~Futures v2 - Inverse Perps~~ | `market: 'inverse'` | The [inverse v2 perps](https://bybit-exchange.github.io/docs/futuresV2/inverse/#t-websocket) category. | -| ~~Futures v2 - USDT Perps~~ | `market: 'linear'` | The [USDT/linear v2 perps](https://bybit-exchange.github.io/docs/futuresV2/linear/#t-websocket) category. | -| ~~Futures v2 - Inverse Futures~~ | `market: 'inverse'` | The [inverse futures v2](https://bybit-exchange.github.io/docs/futuresV2/inverse_futures/#t-websocket) category uses the same market as inverse perps. | -| ~~Spot v3~~ | `market: 'spotv3'` | The [spot v3](https://bybit-exchange.github.io/docs/spot/v3/#t-websocket) category. | -| ~~Spot v1~~ | `market: 'spot'` | The older [spot v1](https://bybit-exchange.github.io/docs/spot/v1/#t-websocket) category. Use the `spotv3` market if possible, as the v1 category does not have automatic re-subscribe if reconnected. | -| ~~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) | - -
{ @@ -72,7 +70,7 @@ interface UnsignedRequest { recvWindow?: number; } -type SignMethod = 'v2auth' | 'v5auth'; +type SignMethod = 'v5auth'; export default abstract class BaseRestClient { private timeOffset: number | null = null; @@ -443,40 +441,6 @@ export default abstract class BaseRestClient { return res; } - // spot/v2 derivatives - if (signMethod === 'v2auth') { - res.originalParams.api_key = key; - res.originalParams.timestamp = timestamp; - - // Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen. - if (recvWindow) { - res.originalParams.recv_window = recvWindow; - } - const sortProperties = true; - const encodeValues = false; - - res.serializedParams = serializeParams( - res.originalParams, - strictParamValidation, - sortProperties, - encodeValues, - ); - res.sign = await this.signMessage( - res.serializedParams, - this.secret, - 'hex', - 'SHA-256', - ); - - // @ts-ignore - res.paramsWithSign = { - ...res.originalParams, - sign: res.sign, - }; - - return res; - } - return res; } diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 751482b..b067c6d 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -1,10 +1,5 @@ import { AxiosResponse } from 'axios'; import { APIRateLimit } from '../types'; -import { - WebsocketSucceededTopicSubscriptionConfirmationEvent, - WebsocketTopicSubscriptionConfirmationEvent, -} from '../types/websockets/ws-confirmations'; -import { WSAPIResponse, WS_API_Operations } from '../types/websockets/ws-api'; export interface RestClientOptions { /** Your API key */ @@ -188,25 +183,6 @@ export function isWsPong(msg: any): boolean { ); } -export function isTopicSubscriptionConfirmation( - msg: unknown, -): msg is WebsocketTopicSubscriptionConfirmationEvent { - if (typeof msg !== 'object') { - return false; - } - if (!msg) { - return false; - } - if (typeof msg['op'] !== 'string') { - return false; - } - if (msg['op'] !== 'subscribe') { - return false; - } - - return true; -} - export const APIID = 'bybitapinode'; /** diff --git a/src/util/typeGuards.ts b/src/util/typeGuards.ts index f4bcc67..f721f99 100644 --- a/src/util/typeGuards.ts +++ b/src/util/typeGuards.ts @@ -2,7 +2,10 @@ * Use type guards to narrow down types with minimal efforts. */ -import { WebsocketSucceededTopicSubscriptionConfirmationEvent } from '../types'; +import { + WebsocketSucceededTopicSubscriptionConfirmationEvent, + WebsocketTopicSubscriptionConfirmationEvent, +} from '../types'; import { WSAPIResponse, WS_API_Operations } from '../types/websockets/ws-api'; import { WSAccountOrderEventV5, @@ -10,7 +13,6 @@ import { WSOrderbookEventV5, WSPositionEventV5, } from '../types/websockets/ws-events'; -import { isTopicSubscriptionConfirmation } from './requestUtils'; /** * Type guard to detect a V5 orderbook event (delta & snapshots) @@ -120,3 +122,22 @@ export function isTopicSubscriptionSuccess( if (!isTopicSubscriptionConfirmation(msg)) return false; return msg.success === true; } + +export function isTopicSubscriptionConfirmation( + msg: unknown, +): msg is WebsocketTopicSubscriptionConfirmationEvent { + if (typeof msg !== 'object') { + return false; + } + if (!msg) { + return false; + } + if (typeof msg['op'] !== 'string') { + return false; + } + if (msg['op'] !== 'subscribe') { + return false; + } + + return true; +} From 0d208224833b495eb981e133bc4ddeb222cae717 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 15:41:34 +0000 Subject: [PATCH 06/23] chore(): update default logger examples --- README.md | 9 --------- examples/demo-trading.ts | 4 ++-- examples/fasterHmacSign.ts | 4 ++-- examples/ws-private-v5.ts | 4 ++-- examples/ws-public-v5.ts | 10 ++-------- test/v5/public.ws.test.ts | 4 +--- 6 files changed, 9 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 52c7927..8f94d49 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs and WebSockets: - [WebSockets](#websockets) - [WebSocket Subscriptions - Consuming Events](#websocket-subscriptions---consuming-events) - [Websocket API - Sending Orders via WebSockets](#websocket-api---sending-orders-via-websockets) -- [Specifying Other Markets](#specifying-other-markets) - [Consumer Load Balancing](#balancing-load-across-multiple-connections) ## Additional Features @@ -485,14 +484,6 @@ See the [examples/ws-api-promises.ts](./examples/ws-api-promises.ts) example for --- -### Specifying other markets - -The WebsocketClient can be configured to a specific API group using the market parameter. These are the currently available API groups: -| API Category | Market | Description | -|:----------------------------: |:-------------------: |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| V5 Subscriptions | `market: 'v5'` | The [v5](https://bybit-exchange.github.io/docs/v5/ws/connect) websocket topics for all categories under one market. Use the subscribeV5 method when subscribing to v5 topics. | - - ### Balancing load across multiple connections The WebsocketClient will automatically prepare one connection per API group, for all topics in that API group. Any topics that you subscribe to on that WebSocket client will automatically be added to the same connection. diff --git a/examples/demo-trading.ts b/examples/demo-trading.ts index 36a0aaa..190e41c 100644 --- a/examples/demo-trading.ts +++ b/examples/demo-trading.ts @@ -29,14 +29,14 @@ const restClient = new RestClientV5({ // Optional, uncomment the "silly" override to log a lot more info about what the WS client is doing const customLogger = { ...DefaultLogger, - // silly: (...params) => console.log('trace', ...params), + // trace: (...params) => console.log('trace', ...params), }; const wsClient = new WebsocketClient( { key: key, secret: secret, - market: 'v5', + /** * Set this to true to enable demo trading for the private account data WS * Topics: order,execution,position,wallet,greeks diff --git a/examples/fasterHmacSign.ts b/examples/fasterHmacSign.ts index 818087d..d9dcca2 100644 --- a/examples/fasterHmacSign.ts +++ b/examples/fasterHmacSign.ts @@ -46,10 +46,10 @@ const restClient = new RestClientV5({ }, }); -// Optional, uncomment the "silly" override to log a lot more info about what the WS client is doing +// Optional, uncomment the "trace" override to log a lot more info about what the WS client is doing const customLogger = { ...DefaultLogger, - // silly: (...params) => console.log('trace', ...params), + // trace: (...params) => console.log('trace', ...params), }; const wsClient = new WebsocketClient( diff --git a/examples/ws-private-v5.ts b/examples/ws-private-v5.ts index e4bb3f1..f9a4f1a 100644 --- a/examples/ws-private-v5.ts +++ b/examples/ws-private-v5.ts @@ -4,10 +4,10 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; // or // import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api'; -// Create & inject a custom logger to disable the silly logging level (empty function) +// Create & inject a custom logger to enable the trace logging level (empty function) const logger = { ...DefaultLogger, - silly: () => {}, + // trace: (...params) => console.log('trace', ...params), }; const key = process.env.API_KEY; diff --git a/examples/ws-public-v5.ts b/examples/ws-public-v5.ts index 6f3e7ca..118181d 100644 --- a/examples/ws-public-v5.ts +++ b/examples/ws-public-v5.ts @@ -5,7 +5,7 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; const logger = { ...DefaultLogger, - silly: (...params) => console.log('silly', ...params), + trace: (...params) => console.log('trace', ...params), }; /** @@ -17,13 +17,7 @@ const logger = { * - Heartbeats/ping/pong/reconnects are all handled automatically. * If a connection drops, the client will clean it up, respawn a fresh connection and resubscribe for you. */ -const wsClient = new WebsocketClient( - { - // Previously required, this parameter is now optional: - // market: 'v5', - }, - logger, -); +const wsClient = new WebsocketClient({}, logger); wsClient.on('update', (data) => { console.log('raw message received ', JSON.stringify(data)); diff --git a/test/v5/public.ws.test.ts b/test/v5/public.ws.test.ts index d86205d..f389411 100644 --- a/test/v5/public.ws.test.ts +++ b/test/v5/public.ws.test.ts @@ -1,9 +1,7 @@ import { WebsocketClient } from '../../src'; describe.skip('Public V5 Websocket client', () => { - const api = new WebsocketClient({ - market: 'v5', - }); + const api = new WebsocketClient({}); const linearSymbol = 'BTCUSDT'; const linearCategory = 'linear'; From 86fc739eca0c692ff7efc95e9f0b586917541952 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 15:46:20 +0000 Subject: [PATCH 07/23] feat(v4.0.0-beta.0): bump version to public beta. WS API integration. Remove deprecated V1-V3 support. WS upgrades. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index db075f5..d40e608 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "3.10.29", + "version": "4.0.0-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "3.10.29", + "version": "4.0.0-beta.0", "license": "MIT", "dependencies": { "axios": "^1.7.9", diff --git a/package.json b/package.json index 6318778..ee4385c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "3.10.29", + "version": "4.0.0-beta.0", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", From 8a692140e02e5002e0a14ea94144653ace0dcc7d Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 16:20:12 +0000 Subject: [PATCH 08/23] fix(4.0.0-beta.1): fix backwards compatibility with req_id usage for ws subscribe/unsubscribe --- package-lock.json | 4 ++-- package.json | 2 +- src/util/BaseRestClient.ts | 1 - src/websocket-client.ts | 12 ++++++++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index d40e608..34c5f81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "4.0.0-beta.0", + "version": "4.0.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "4.0.0-beta.0", + "version": "4.0.0-beta.1", "license": "MIT", "dependencies": { "axios": "^1.7.9", diff --git a/package.json b/package.json index ee4385c..83b6a1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "4.0.0-beta.0", + "version": "4.0.0-beta.1", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/util/BaseRestClient.ts b/src/util/BaseRestClient.ts index 8a6dc0f..fb34e64 100644 --- a/src/util/BaseRestClient.ts +++ b/src/util/BaseRestClient.ts @@ -4,7 +4,6 @@ import https from 'https'; import { APIID, - REST_CLIENT_TYPE_ENUM, RestClientOptions, RestClientType, getRestBaseUrl, diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 3ed2ccf..d26df78 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -550,10 +550,18 @@ export class WebsocketClient extends BaseWebsocketClient< switch (market) { case 'all': { + const topics = requests.map((r) => r.topic); + + // Previously used to track topics in a request. Keeping this for subscribe/unsubscribe requests, no need for incremental values + const req_id = + ['subscribe', 'unsubscribe'].includes(operation) && topics.length + ? topics.join('_') + : this.getNewRequestId(); + const wsEvent: WsRequestOperationBybit = { - req_id: this.getNewRequestId(), + req_id: req_id, op: operation, - args: requests.map((r) => r.topic), + args: topics, }; const midflightWsEvent: MidflightWsRequestEvent< From 1d358e0896b97e03e2976589b0917c2226f745bc Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Fri, 24 Jan 2025 16:26:24 +0000 Subject: [PATCH 09/23] fix(v4.0.0-beta.2): separator for req_id topic list --- package-lock.json | 4 ++-- package.json | 2 +- src/websocket-client.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 34c5f81..e9c6d34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "license": "MIT", "dependencies": { "axios": "^1.7.9", diff --git a/package.json b/package.json index 83b6a1f..53cf797 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/websocket-client.ts b/src/websocket-client.ts index d26df78..c19de74 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -555,7 +555,7 @@ export class WebsocketClient extends BaseWebsocketClient< // Previously used to track topics in a request. Keeping this for subscribe/unsubscribe requests, no need for incremental values const req_id = ['subscribe', 'unsubscribe'].includes(operation) && topics.length - ? topics.join('_') + ? topics.join(',') : this.getNewRequestId(); const wsEvent: WsRequestOperationBybit = { From 5833d0679b3a4da41fe65b5e71efcc90af7e8f3f Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 25 Jan 2025 01:10:30 +0000 Subject: [PATCH 10/23] fix(): submit order example --- examples/apidoc/V5/Trade/place-order.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/apidoc/V5/Trade/place-order.js b/examples/apidoc/V5/Trade/place-order.js index 1cba6bb..4669776 100644 --- a/examples/apidoc/V5/Trade/place-order.js +++ b/examples/apidoc/V5/Trade/place-order.js @@ -6,18 +6,31 @@ const client = new RestClientV5({ secret: 'apisecret', }); +// Submit a market order client .submitOrder({ category: 'spot', symbol: 'BTCUSDT', side: 'Buy', orderType: 'Market', - qty: '0.1', - price: '15600', - timeInForce: 'PostOnly', - orderLinkId: 'spot-test-postonly', - isLeverage: 0, - orderFilter: 'Order', + qty: '1', + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.error(error); + }); + +// Submit a limit order +client + .submitOrder({ + category: 'spot', + symbol: 'BTCUSDT', + side: 'Buy', + orderType: 'Limit', + qty: '1', + price: '55000', }) .then((response) => { console.log(response); From b4bdb75c6d5649d5dcab3b7b84eb656eee5a1c73 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 25 Jan 2025 01:11:54 +0000 Subject: [PATCH 11/23] chore(): improve submit order example --- examples/apidoc/V5/Trade/place-order.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/apidoc/V5/Trade/place-order.js b/examples/apidoc/V5/Trade/place-order.js index 4669776..f4d38cf 100644 --- a/examples/apidoc/V5/Trade/place-order.js +++ b/examples/apidoc/V5/Trade/place-order.js @@ -16,10 +16,10 @@ client qty: '1', }) .then((response) => { - console.log(response); + console.log('Market order result', response); }) .catch((error) => { - console.error(error); + console.error('Market order error', error); }); // Submit a limit order @@ -33,8 +33,8 @@ client price: '55000', }) .then((response) => { - console.log(response); + console.log('Limit order result', response); }) .catch((error) => { - console.error(error); + console.error('Limit order error', error); }); From e2a91d89f7606b0b4957f071c693bf25f0650d2d Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 25 Jan 2025 12:14:54 +0000 Subject: [PATCH 12/23] v3.10.30: add missing WSExecutionV5 execPnl property. Bump npm audit dependency. --- package-lock.json | 17 +++++++++-------- package.json | 2 +- src/types/websocket.events.ts | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07353ab..d0632e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "3.10.28", + "version": "3.10.30", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "3.10.28", + "version": "3.10.30", "license": "MIT", "dependencies": { "axios": "^1.6.6", @@ -2714,10 +2714,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "devOptional": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -9107,9 +9108,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "devOptional": true, "requires": { "path-key": "^3.1.0", diff --git a/package.json b/package.json index 7363a0e..6d71d26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "3.10.29", + "version": "3.10.30", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/types/websocket.events.ts b/src/types/websocket.events.ts index 8c57f7a..740d0c1 100644 --- a/src/types/websocket.events.ts +++ b/src/types/websocket.events.ts @@ -184,6 +184,7 @@ export interface WSExecutionV5 { execId: string; execPrice: string; execQty: string; + execPnl: string; execType: ExecTypeV5; execValue: string; execTime: string; From d0511426b1b1b416f86c39c03f4ba96348533c22 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 1 Feb 2025 15:27:41 +0000 Subject: [PATCH 13/23] chore(): misc cleaning & note around exception emitter, fix(#411): only resolve demo trading url for private topics --- src/util/BaseWSClient.ts | 80 ++++++++++++++++++++------- src/util/websockets/websocket-util.ts | 12 ++-- 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index 43cd23e..59657da 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -38,8 +38,17 @@ interface WSClientEventMap { ) => void; /** Received data for topic */ update: (response: any & { wsKey: WsKey }) => void; - /** Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) */ + /** + * Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) + * @deprecated Use 'exception' instead. The 'error' event had the unintended consequence of throwing an unhandled promise rejection. + */ error: (response: any & { wsKey: WsKey; isWSAPIResponse?: boolean }) => void; + /** + * Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) + */ + exception: ( + response: any & { wsKey: WsKey; isWSAPIResponse?: boolean }, + ) => void; /** Confirmation that a connection successfully authenticated */ authenticated: (event: { wsKey: WsKey; @@ -71,14 +80,6 @@ export interface BaseWebsocketClient< ): boolean; } -// interface TopicsPendingSubscriptions { -// wsKey: string; -// failedTopicsSubscriptions: Set; -// pendingTopicsSubscriptions: Set; -// resolver: TopicsPendingSubscriptionsResolver; -// rejector: TopicsPendingSubscriptionsRejector; -// } - /** * A midflight WS request event (e.g. subscribe to these topics). * @@ -168,8 +169,10 @@ export abstract class BaseWebsocketClient< }; // add default error handling so this doesn't crash node (if the user didn't set a handler) - // eslint-disable-next-line @typescript-eslint/no-empty-function - this.on('error', () => {}); + // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars, no-unused-vars + this.on('error', (e) => { + // console.log('basewserr: ', e); + }); } /** @@ -339,10 +342,18 @@ export abstract class BaseWebsocketClient< pendingSubscriptionRequest.requestData, ); } else { - pendingSubscriptionRequest.rejector( - pendingSubscriptionRequest.requestData, + this.logger.trace( + `updatePendingTopicSubscriptionStatus.reject(${wsKey}, ${requestKey}, ${msg}, ${isTopicSubscriptionSuccessEvent}): `, msg, ); + try { + pendingSubscriptionRequest.rejector( + pendingSubscriptionRequest.requestData, + msg, + ); + } catch (e) { + console.error('Exception rejecting promise: ', e); + } } this.removeTopicPendingSubscription(wsKey, requestKey); @@ -614,6 +625,8 @@ export abstract class BaseWebsocketClient< break; } + this.logger.error(`parseWsError(${context}, ${error}, ${wsKey}) `, error); + this.emit('response', { ...error, wsKey }); this.emit('error', { ...error, wsKey }); } @@ -1008,12 +1021,28 @@ export abstract class BaseWebsocketClient< ); // Request sub to public topics, if any - this.requestSubscribeTopics(wsKey, publicReqs); + try { + await this.requestSubscribeTopics(wsKey, publicReqs); + } catch (e) { + this.logger.error( + `onWsOpen(): exception in public requestSubscribeTopics(${wsKey}): `, + publicReqs, + e, + ); + } // Request sub to private topics, if auth on connect isn't needed // Else, this is automatic after authentication is successfully confirmed if (!this.options.authPrivateConnectionsOnConnect) { - this.requestSubscribeTopics(wsKey, privateReqs); + try { + this.requestSubscribeTopics(wsKey, privateReqs); + } catch (e) { + this.logger.error( + `onWsOpen(): exception in private requestSubscribeTopics(${wsKey}: `, + privateReqs, + e, + ); + } } // Some websockets require an auth packet to be sent after opening the connection @@ -1114,10 +1143,6 @@ export abstract class BaseWebsocketClient< } for (const emittable of emittableEvents) { - // if (emittable.event?.op) { - // console.log('emittable: ', emittable); - // } - if (this.isWsPong(emittable)) { this.logger.trace('Received pong2', { ...WS_LOGGER_CATEGORY, @@ -1143,7 +1168,22 @@ export abstract class BaseWebsocketClient< continue; } - this.emit(emittable.eventType, emittableFinalEvent); + // this.logger.trace( + // `onWsMessage().emit(${emittable.eventType})`, + // emittableFinalEvent, + // ); + try { + this.emit(emittable.eventType, emittableFinalEvent); + } catch (e) { + this.logger.error( + `Exception in onWsMessage().emit(${emittable.eventType}) handler:`, + e, + ); + } + // this.logger.trace( + // `onWsMessage().emit(${emittable.eventType}).done()`, + // emittableFinalEvent, + // ); } return; diff --git a/src/util/websockets/websocket-util.ts b/src/util/websockets/websocket-util.ts index 51c4b20..b979aa4 100644 --- a/src/util/websockets/websocket-util.ts +++ b/src/util/websockets/websocket-util.ts @@ -241,19 +241,23 @@ export function getWsUrl( } // https://bybit-exchange.github.io/docs/v5/demo - const isDemoTrading = wsClientOptions.demoTrading; - if (isDemoTrading) { - return 'wss://stream-demo.bybit.com/v5/private'; - } + const demoTradingPrivateEndpoint = 'wss://stream-demo.bybit.com/v5/private'; + const isDemoTrading = wsClientOptions.demoTrading; const isTestnet = wsClientOptions.testnet; const networkKey = isTestnet ? 'testnet' : 'livenet'; switch (wsKey) { case WS_KEY_MAP.v5Private: { + if (isDemoTrading) { + return demoTradingPrivateEndpoint; + } return WS_BASE_URL_MAP.v5.private[networkKey]; } case WS_KEY_MAP.v5PrivateTrade: { + if (isDemoTrading) { + return demoTradingPrivateEndpoint; + } return WS_BASE_URL_MAP[wsKey].private[networkKey]; } case WS_KEY_MAP.v5SpotPublic: { From bbf3a7224c456d1ee9d24e48599a20522dbc5550 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 1 Feb 2025 15:31:25 +0000 Subject: [PATCH 14/23] fix(v3.10.31, #411): demo trading wss should only be used for private topics. Fixes #411. --- examples/ws-public-v5.ts | 1 + package-lock.json | 4 ++-- package.json | 2 +- src/util/websocket-util.ts | 10 +++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/ws-public-v5.ts b/examples/ws-public-v5.ts index 9a8dff8..e7b08e4 100644 --- a/examples/ws-public-v5.ts +++ b/examples/ws-public-v5.ts @@ -20,6 +20,7 @@ const logger = { const wsClient = new WebsocketClient( { market: 'v5', + // demoTrading: true, }, logger, ); diff --git a/package-lock.json b/package-lock.json index d0632e6..3f9ed24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "3.10.30", + "version": "3.10.31", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "3.10.30", + "version": "3.10.31", "license": "MIT", "dependencies": { "axios": "^1.6.6", diff --git a/package.json b/package.json index 6d71d26..35bdc94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "3.10.30", + "version": "3.10.31", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index b6084d3..5b3c02d 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -406,12 +406,7 @@ export function getWsUrl( return wsUrl; } - // https://bybit-exchange.github.io/docs/v5/demo const isDemoTrading = wsClientOptions.demoTrading; - if (isDemoTrading) { - return 'wss://stream-demo.bybit.com/v5/private'; - } - const isTestnet = wsClientOptions.testnet; const networkKey = isTestnet ? 'testnet' : 'livenet'; @@ -475,6 +470,11 @@ export function getWsUrl( return WS_BASE_URL_MAP.contractUSDT.public[networkKey]; } case WS_KEY_MAP.v5Private: { + // https://bybit-exchange.github.io/docs/v5/demo + if (isDemoTrading) { + return 'wss://stream-demo.bybit.com/v5/private'; + } + return WS_BASE_URL_MAP.v5.private[networkKey]; } case WS_KEY_MAP.v5SpotPublic: { From 57b1a72b7f7777c660f211bd30f290a0180022d5 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Thu, 6 Feb 2025 12:08:51 +0000 Subject: [PATCH 15/23] feat(v4.0.0-beta.5): BREAKING CHANGE: rename "error" event to "exception" to avoid unhandled exceptions --- examples/demo-trading.ts | 2 +- examples/fasterHmacSign.ts | 2 +- examples/ws-api-promises.ts | 4 ++-- examples/ws-public-v5.ts | 15 ++++++--------- package-lock.json | 4 ++-- package.json | 2 +- src/util/BaseWSClient.ts | 30 +++++++++++++----------------- src/websocket-client.ts | 8 ++++---- test/v5/public.ws.test.ts | 10 +++++++--- test/ws.util.ts | 10 +++++----- 10 files changed, 42 insertions(+), 45 deletions(-) diff --git a/examples/demo-trading.ts b/examples/demo-trading.ts index 190e41c..b4002a0 100644 --- a/examples/demo-trading.ts +++ b/examples/demo-trading.ts @@ -91,7 +91,7 @@ function setWsClientEventListeners( websocketClient.on('reconnected', (data) => { console.log(new Date(), accountRef, 'ws has reconnected ', data?.wsKey); }); - websocketClient.on('error', (data) => { + websocketClient.on('exception', (data) => { console.error(new Date(), accountRef, 'ws exception: ', data); }); }); diff --git a/examples/fasterHmacSign.ts b/examples/fasterHmacSign.ts index d9dcca2..99b3f4f 100644 --- a/examples/fasterHmacSign.ts +++ b/examples/fasterHmacSign.ts @@ -117,7 +117,7 @@ function setWsClientEventListeners( websocketClient.on('reconnected', (data) => { console.log(new Date(), accountRef, 'ws has reconnected ', data?.wsKey); }); - websocketClient.on('error', (data) => { + websocketClient.on('exception', (data) => { console.error(new Date(), accountRef, 'ws exception: ', data); }); }); diff --git a/examples/ws-api-promises.ts b/examples/ws-api-promises.ts index 5be77d9..4ccf41e 100644 --- a/examples/ws-api-promises.ts +++ b/examples/ws-api-promises.ts @@ -40,8 +40,8 @@ wsClient.on('reconnected', (data) => { wsClient.on('authenticated', (data) => { console.log('ws has authenticated ', data?.wsKey); }); -wsClient.on('error', (data) => { - console.error('ws error: ', data); +wsClient.on('exception', (data) => { + console.error('ws exception: ', data); }); async function main() { diff --git a/examples/ws-public-v5.ts b/examples/ws-public-v5.ts index 3fe50a4..1feea00 100644 --- a/examples/ws-public-v5.ts +++ b/examples/ws-public-v5.ts @@ -17,12 +17,8 @@ const logger = { * - Heartbeats/ping/pong/reconnects are all handled automatically. * If a connection drops, the client will clean it up, respawn a fresh connection and resubscribe for you. */ -const wsClient = new WebsocketClient( - { - // demoTrading: true, - }, - logger, -); + +const wsClient = new WebsocketClient(); wsClient.on('update', (data) => { console.log('raw message received ', JSON.stringify(data)); @@ -40,9 +36,10 @@ wsClient.on('reconnect', ({ wsKey }) => { wsClient.on('reconnected', (data) => { console.log('ws has reconnected ', data?.wsKey); }); -// wsClient.on('error', (data) => { -// console.error('ws exception: ', data); -// }); + +wsClient.on('exception', (data) => { + console.error('ws exception: ', data); +}); /** * For public V5 topics, use the subscribeV5 method and include the API category this topic is for. diff --git a/package-lock.json b/package-lock.json index c3edbf4..35b92ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "license": "MIT", "dependencies": { "axios": "^1.7.9", diff --git a/package.json b/package.json index 8018b86..ae5ab4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "4.0.0-beta.4", + "version": "4.0.0-beta.5", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index 59657da..f344907 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -23,6 +23,8 @@ import { } from './websockets'; import { WsOperation } from '../types/websockets/ws-api'; +type UseTheExceptionEventInstead = never; + interface WSClientEventMap { /** Connection opened. If this connection was previously opened and reconnected, expect the reconnected event instead */ open: (evt: { wsKey: WsKey; event: any }) => void; @@ -39,10 +41,10 @@ interface WSClientEventMap { /** Received data for topic */ update: (response: any & { wsKey: WsKey }) => void; /** - * Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) - * @deprecated Use 'exception' instead. The 'error' event had the unintended consequence of throwing an unhandled promise rejection. + * See for more information: https://github.com/tiagosiebler/bybit-api/issues/413 + * @deprecated Use the 'exception' event instead. The 'error' event had the unintended consequence of throwing an unhandled promise rejection. */ - error: (response: any & { wsKey: WsKey; isWSAPIResponse?: boolean }) => void; + error: UseTheExceptionEventInstead; /** * Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) */ @@ -57,12 +59,6 @@ interface WSClientEventMap { }) => void; } -export interface EmittableEvent { - eventType: 'response' | 'update' | 'error' | 'authenticated'; - event: TEvent; - isWSAPIResponse?: boolean; -} - // Type safety for on and emit handlers: https://stackoverflow.com/a/61609010/880837 export interface BaseWebsocketClient< TWSKey extends string, @@ -80,6 +76,12 @@ export interface BaseWebsocketClient< ): boolean; } +export interface EmittableEvent { + eventType: 'response' | 'update' | 'exception' | 'authenticated'; + event: TEvent; + isWSAPIResponse?: boolean; +} + /** * A midflight WS request event (e.g. subscribe to these topics). * @@ -167,12 +169,6 @@ export abstract class BaseWebsocketClient< authPrivateRequests: false, ...options, }; - - // add default error handling so this doesn't crash node (if the user didn't set a handler) - // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars, no-unused-vars - this.on('error', (e) => { - // console.log('basewserr: ', e); - }); } /** @@ -593,7 +589,7 @@ export abstract class BaseWebsocketClient< if (!error.message) { this.logger.error(`${context} due to unexpected error: `, error); this.emit('response', { ...error, wsKey }); - this.emit('error', { ...error, wsKey }); + this.emit('exception', { ...error, wsKey }); return; } @@ -628,7 +624,7 @@ export abstract class BaseWebsocketClient< this.logger.error(`parseWsError(${context}, ${error}, ${wsKey}) `, error); this.emit('response', { ...error, wsKey }); - this.emit('error', { ...error, wsKey }); + this.emit('exception', { ...error, wsKey }); } /** Get a signature, build the auth request and send it */ diff --git a/src/websocket-client.ts b/src/websocket-client.ts index c19de74..d87bbd0 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -136,7 +136,7 @@ export class WebsocketClient extends BaseWebsocketClient< perWsKeyTopics[derivedWsKey] = []; } - perWsKeyTopics[derivedWsKey].push(wsRequest); + perWsKeyTopics[derivedWsKey]!.push(wsRequest); } const promises: Promise[] = []; @@ -755,7 +755,7 @@ export class WebsocketClient extends BaseWebsocketClient< } results.push({ - eventType: 'error', + eventType: 'exception', event: parsed, isWSAPIResponse: true, }); @@ -804,7 +804,7 @@ export class WebsocketClient extends BaseWebsocketClient< // Failed request if (parsed.success === false) { results.push({ - eventType: 'error', + eventType: 'exception', event: parsed, }); return results; @@ -851,7 +851,7 @@ export class WebsocketClient extends BaseWebsocketClient< exception: e, eventData: event.data, }, - eventType: 'error', + eventType: 'exception', }); this.logger.error('Failed to parse event data due to exception: ', { diff --git a/test/v5/public.ws.test.ts b/test/v5/public.ws.test.ts index f389411..3d35898 100644 --- a/test/v5/public.ws.test.ts +++ b/test/v5/public.ws.test.ts @@ -9,13 +9,17 @@ describe.skip('Public V5 Websocket client', () => { describe('Topics subscription confirmation', () => { it('can subscribeV5 to LINEAR with valid topic', async () => { await expect( - api.subscribeV5(`publicTrade.${linearSymbol}`, linearCategory), - ).resolves.toBeUndefined(); + Promise.allSettled( + api.subscribeV5(`publicTrade.${linearSymbol}`, linearCategory), + ), + ).resolves.toStrictEqual([]); }); it('cannot subscribeV5 to LINEAR with valid topic', async () => { try { - await api.subscribeV5(`publicTrade.${linearSymbol}X`, linearCategory); + await Promise.allSettled( + api.subscribeV5(`publicTrade.${linearSymbol}X`, linearCategory), + ); } catch (e) { expect(e).toBeDefined(); expect(e).toMatch(`(publicTrade.${linearSymbol}X) failed to subscribe`); diff --git a/test/ws.util.ts b/test/ws.util.ts index a8c1534..db82eee 100644 --- a/test/ws.util.ts +++ b/test/ws.util.ts @@ -62,7 +62,7 @@ export function waitForSocketEvent( } wsClient.on(event, (e) => resolver(e)); - wsClient.on('error', (e) => rejector(e)); + wsClient.on('exception', (e) => rejector(e)); // if (event !== 'close') { // wsClient.on('close', (event) => { @@ -78,21 +78,21 @@ export function waitForSocketEvent( export function listenToSocketEvents(wsClient: WebsocketClient) { const retVal: Record< - 'update' | 'open' | 'response' | 'close' | 'error', + 'update' | 'open' | 'response' | 'close' | 'exception', typeof jest.fn > = { open: jest.fn(), response: jest.fn(), update: jest.fn(), close: jest.fn(), - error: jest.fn(), + exception: jest.fn(), }; wsClient.on('open', retVal.open); wsClient.on('response', retVal.response); wsClient.on('update', retVal.update); wsClient.on('close', retVal.close); - wsClient.on('error', retVal.error); + wsClient.on('exception', retVal.exception); return { ...retVal, @@ -101,7 +101,7 @@ export function listenToSocketEvents(wsClient: WebsocketClient) { wsClient.removeListener('response', retVal.response); wsClient.removeListener('update', retVal.update); wsClient.removeListener('close', retVal.close); - wsClient.removeListener('error', retVal.error); + wsClient.removeListener('exception', retVal.exception); }, }; } From 2f18edec02b3bad58887e98b8302425dbbddd121 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Thu, 6 Feb 2025 12:14:28 +0000 Subject: [PATCH 16/23] chore(): add logger injection to example --- examples/ws-public-v5.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ws-public-v5.ts b/examples/ws-public-v5.ts index 1feea00..99d37db 100644 --- a/examples/ws-public-v5.ts +++ b/examples/ws-public-v5.ts @@ -18,7 +18,7 @@ const logger = { * If a connection drops, the client will clean it up, respawn a fresh connection and resubscribe for you. */ -const wsClient = new WebsocketClient(); +const wsClient = new WebsocketClient({}, logger); wsClient.on('update', (data) => { console.log('raw message received ', JSON.stringify(data)); From 1abc2425e438209a27a4e710af1ed75b3db3cfd1 Mon Sep 17 00:00:00 2001 From: JJ-Cro Date: Mon, 10 Feb 2025 14:03:08 +0100 Subject: [PATCH 17/23] feat(v3.10.32): add params for GetDeliveryRecord, fixed e2e tests --- .github/workflows/e2etest.yml | 23 +++++++++++------------ package-lock.json | 4 ++-- package.json | 2 +- src/types/request/v5-asset.ts | 2 ++ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/e2etest.yml b/.github/workflows/e2etest.yml index 38ef71c..9518c15 100644 --- a/.github/workflows/e2etest.yml +++ b/.github/workflows/e2etest.yml @@ -1,16 +1,12 @@ -name: 'Build & Test' +name: E2ETests # Changed to match KuCoin naming -on: [push] - -# on: -# # pull_request: -# # branches: -# # - "master" -# push: -# branches: +on: + push: + pull_request: # Added pull_request trigger + workflow_dispatch: # Added manual trigger option jobs: - build: + E2ETests: # Changed job ID to match KuCoin name: 'Build & Test' runs-on: ubuntu-latest @@ -30,8 +26,11 @@ jobs: - name: Build run: npm run build - - name: Test - run: npm run test + - name: Test Public API Calls # Split test command into public and private + run: npm run test -- public.test.ts + + - name: Test Private API Calls # Added separate step for private API tests + run: npm run test -- private.test.ts env: API_KEY_COM: ${{ secrets.API_KEY_COM }} API_SECRET_COM: ${{ secrets.API_SECRET_COM }} diff --git a/package-lock.json b/package-lock.json index 3f9ed24..d68a2ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "3.10.31", + "version": "3.10.32", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "3.10.31", + "version": "3.10.32", "license": "MIT", "dependencies": { "axios": "^1.6.6", diff --git a/package.json b/package.json index 35bdc94..f3fc2d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "3.10.31", + "version": "3.10.32", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/types/request/v5-asset.ts b/src/types/request/v5-asset.ts index 3de59cd..7e22199 100644 --- a/src/types/request/v5-asset.ts +++ b/src/types/request/v5-asset.ts @@ -10,6 +10,8 @@ export interface GetCoinExchangeRecordParamsV5 { export interface GetDeliveryRecordParamsV5 { category: CategoryV5; symbol?: string; + startTime?: number; + endTime?: number; expDate?: string; limit?: number; cursor?: string; From 0a4104dd9158596251fb991e6ed3162079ebbd43 Mon Sep 17 00:00:00 2001 From: JJ-Cro Date: Mon, 10 Feb 2025 14:23:02 +0100 Subject: [PATCH 18/23] fix(): updated test file names --- .github/workflows/e2etest.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2etest.yml b/.github/workflows/e2etest.yml index 9518c15..f95902d 100644 --- a/.github/workflows/e2etest.yml +++ b/.github/workflows/e2etest.yml @@ -26,11 +26,18 @@ jobs: - name: Build run: npm run build - - name: Test Public API Calls # Split test command into public and private - run: npm run test -- public.test.ts + - name: Test Public Read API Calls + run: npm run test -- public.read.test.ts + + - name: Test Private Read API Calls + run: npm run test -- private.read.test.ts + + - name: Test Private Write API Calls + run: npm run test -- private.write.test.ts + + - name: Test Public WS + run: npm run test -- public.ws.test.ts - - name: Test Private API Calls # Added separate step for private API tests - run: npm run test -- private.test.ts env: API_KEY_COM: ${{ secrets.API_KEY_COM }} API_SECRET_COM: ${{ secrets.API_SECRET_COM }} From 5b981d05e31c6d3e8e5ee8909541fef20294f336 Mon Sep 17 00:00:00 2001 From: JJ-Cro Date: Mon, 10 Feb 2025 14:53:40 +0100 Subject: [PATCH 19/23] chore(): removed unnecesary comments --- .github/workflows/e2etest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2etest.yml b/.github/workflows/e2etest.yml index f95902d..47d3d62 100644 --- a/.github/workflows/e2etest.yml +++ b/.github/workflows/e2etest.yml @@ -1,12 +1,12 @@ -name: E2ETests # Changed to match KuCoin naming +name: E2ETests on: push: - pull_request: # Added pull_request trigger - workflow_dispatch: # Added manual trigger option + pull_request: + workflow_dispatch: jobs: - E2ETests: # Changed job ID to match KuCoin + E2ETests: name: 'Build & Test' runs-on: ubuntu-latest From c9dbded2a28d375c312c9e355552dee2e7c45b5a Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Mon, 10 Feb 2025 13:57:46 +0000 Subject: [PATCH 20/23] chore(): fix env vars for test split --- .github/workflows/e2etest.yml | 41 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/e2etest.yml b/.github/workflows/e2etest.yml index 47d3d62..e50548c 100644 --- a/.github/workflows/e2etest.yml +++ b/.github/workflows/e2etest.yml @@ -1,12 +1,20 @@ -name: E2ETests +name: E2ETests on: push: - pull_request: - workflow_dispatch: + pull_request: + workflow_dispatch: + +env: + PROXY_ENABLED: ${{ secrets.PROXY_ENABLED }} + PROXY_HOST: ${{ secrets.PROXY_HOST }} + PROXY_PASS: ${{ secrets.PROXY_PASS }} + PROXY_PORT: ${{ secrets.PROXY_PORT }} + PROXY_USER: ${{ secrets.PROXY_USER }} + jobs: - E2ETests: + E2ETests: name: 'Build & Test' runs-on: ubuntu-latest @@ -26,23 +34,20 @@ jobs: - name: Build run: npm run build - - name: Test Public Read API Calls + - name: Test Public Read API Calls run: npm run test -- public.read.test.ts - - name: Test Private Read API Calls + - name: Test Private Read API Calls run: npm run test -- private.read.test.ts - - - name: Test Private Write API Calls - run: npm run test -- private.write.test.ts - - - name: Test Public WS - run: npm run test -- public.ws.test.ts - env: API_KEY_COM: ${{ secrets.API_KEY_COM }} API_SECRET_COM: ${{ secrets.API_SECRET_COM }} - PROXY_ENABLED: ${{ secrets.PROXY_ENABLED }} - PROXY_HOST: ${{ secrets.PROXY_HOST }} - PROXY_PASS: ${{ secrets.PROXY_PASS }} - PROXY_PORT: ${{ secrets.PROXY_PORT }} - PROXY_USER: ${{ secrets.PROXY_USER }} + + - name: Test Private Write API Calls + run: npm run test -- private.write.test.ts + env: + API_KEY_COM: ${{ secrets.API_KEY_COM }} + API_SECRET_COM: ${{ secrets.API_SECRET_COM }} + + - name: Test Public WS + run: npm run test -- public.ws.test.ts From addfd16fa2dcd6ff04a0ef6b8b0c085c679ae7ab Mon Sep 17 00:00:00 2001 From: JJ-Cro Date: Thu, 13 Feb 2025 15:50:49 +0100 Subject: [PATCH 21/23] feat(3.10.33): Updated return types for getSubDepositAddress, createUniversalTransfer and createInternalTransfer --- package-lock.json | 4 ++-- package.json | 2 +- src/rest-client-v5.ts | 28 ++++++++++++++++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index d68a2ad..2fe238b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "3.10.32", + "version": "3.10.33", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "3.10.32", + "version": "3.10.33", "license": "MIT", "dependencies": { "axios": "^1.6.6", diff --git a/package.json b/package.json index f3fc2d9..a7fa092 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "3.10.32", + "version": "3.10.33", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/rest-client-v5.ts b/src/rest-client-v5.ts index 0262485..89531ae 100644 --- a/src/rest-client-v5.ts +++ b/src/rest-client-v5.ts @@ -57,7 +57,7 @@ import { DeleteSubMemberParamsV5, DeliveryPriceV5, DeliveryRecordV5, - DepositAddressResultV5, + DepositAddressChainV5, DepositRecordV5, ExchangeBrokerAccountInfoV5, ExchangeBrokerEarningResultV5, @@ -1305,7 +1305,7 @@ export class RestClientV5 extends BaseRestClient { amount: string, fromAccountType: AccountTypeV5, toAccountType: AccountTypeV5, - ): Promise> { + ): Promise> { return this.postPrivate('/v5/asset/transfer/inter-transfer', { transferId, coin, @@ -1365,7 +1365,7 @@ export class RestClientV5 extends BaseRestClient { */ createUniversalTransfer( params: UniversalTransferParamsV5, - ): Promise> { + ): Promise> { return this.postPrivate('/v5/asset/transfer/universal-transfer', params); } @@ -1464,7 +1464,12 @@ export class RestClientV5 extends BaseRestClient { getMasterDepositAddress( coin: string, chainType?: string, - ): Promise> { + ): Promise< + APIResponseV3WithTime<{ + coin: string; + chains: DepositAddressChainV5[]; + }> + > { return this.getPrivate('/v5/asset/deposit/query-address', { coin, chainType, @@ -1478,7 +1483,12 @@ export class RestClientV5 extends BaseRestClient { coin: string, chainType: string, subMemberId: string, - ): Promise> { + ): Promise< + APIResponseV3WithTime<{ + coin: string; + chains: DepositAddressChainV5; + }> + > { return this.getPrivate('/v5/asset/deposit/query-sub-member-address', { coin, chainType, @@ -1488,6 +1498,7 @@ export class RestClientV5 extends BaseRestClient { /** * Query the deposit address information of SUB account. + * @deprecated Duplicate endpoint - Use getSubDepositAddress() instead * * CAUTION * Can use master UID's api key only @@ -1496,7 +1507,12 @@ export class RestClientV5 extends BaseRestClient { coin: string, chainType: string, subMemberId: string, - ): Promise> { + ): Promise< + APIResponseV3<{ + coin: string; + chains: DepositAddressChainV5; + }> + > { return this.getPrivate('/v5/asset/deposit/query-sub-member-address', { coin, chainType, From 912093245df3b1a97b61b9138214ca92da8d402f Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Mon, 17 Feb 2025 11:00:20 +0000 Subject: [PATCH 22/23] feat(): optional promisified subscribe requests --- src/types/websockets/ws-general.ts | 9 +++++++++ src/util/BaseWSClient.ts | 32 ++++++++++++++++++++++++------ src/util/websockets/WsStore.ts | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/types/websockets/ws-general.ts b/src/types/websockets/ws-general.ts index bd4d52e..7a61f3d 100644 --- a/src/types/websockets/ws-general.ts +++ b/src/types/websockets/ws-general.ts @@ -123,6 +123,15 @@ export interface WSClientConfigurableOptions { wsUrl?: string; + /** + * Default: false. + * + * When enabled, any calls to the subscribe method will return a promise. + * Note: internally, subscription requests are sent in batches. This may not behave as expected when + * subscribing to a large number of topics, especially if you are not yet connected when subscribing. + */ + promiseSubscribeRequests?: boolean; + /** * Allows you to provide a custom "signMessage" function, e.g. to use node's much faster createHmac method * diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index f344907..24b7f76 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -163,10 +163,15 @@ export abstract class BaseWebsocketClient< reconnectTimeout: 500, recvWindow: 5000, + // Calls to subscribeV5() are wrapped in a promise, allowing you to await a subscription request. + // Note: due to internal complexity, it's only recommended if you connect before subscribing. + promiseSubscribeRequests: false, + // Automatically send an authentication op/request after a connection opens, for private connections. authPrivateConnectionsOnConnect: true, // Individual requests do not require a signature, so this is disabled. authPrivateRequests: false, + ...options, }; } @@ -305,6 +310,9 @@ export abstract class BaseWebsocketClient< for (const requestKey in pendingSubReqs) { const request = pendingSubReqs[requestKey]; + this.logger.trace( + `clearTopicsPendingSubscriptions(${wsKey}, ${rejectAll}, ${rejectReason}, ${requestKey}): rejecting promise for: ${JSON.stringify(request?.requestData || {})}`, + ); request?.rejector(request.requestData, rejectReason); } } @@ -854,9 +862,11 @@ export abstract class BaseWebsocketClient< for (const midflightRequest of subscribeWsMessages) { const wsMessage = midflightRequest.requestEvent; - promises.push( - this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest), - ); + if (this.options.promiseSubscribeRequests) { + promises.push( + this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest), + ); + } this.logger.trace( `Sending batch via message: "${JSON.stringify(wsMessage)}"`, @@ -899,9 +909,11 @@ export abstract class BaseWebsocketClient< for (const midflightRequest of subscribeWsMessages) { const wsMessage = midflightRequest.requestEvent; - promises.push( - this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest), - ); + if (this.options.promiseSubscribeRequests) { + promises.push( + this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest), + ); + } this.logger.trace(`Sending batch via message: "${wsMessage}"`); this.tryWsSend(wsKey, JSON.stringify(wsMessage)); @@ -1004,6 +1016,7 @@ export abstract class BaseWebsocketClient< } catch (e) { this.logger.error( 'Exception trying to resolve "connectionInProgress" promise', + e, ); } @@ -1073,6 +1086,7 @@ export abstract class BaseWebsocketClient< } catch (e) { this.logger.error( 'Exception trying to resolve "connectionInProgress" promise', + e, ); } @@ -1216,6 +1230,9 @@ export abstract class BaseWebsocketClient< if ( this.wsStore.getConnectionState(wsKey) !== WsConnectionStateEnum.CLOSING ) { + this.logger.trace( + `onWsClose(${wsKey}): rejecting all deferred promises...`, + ); // clean up any pending promises for this connection this.getWsStore().rejectAllDeferredPromises( wsKey, @@ -1230,6 +1247,9 @@ export abstract class BaseWebsocketClient< this.emit('reconnect', { wsKey, event }); } else { // clean up any pending promises for this connection + this.logger.trace( + `onWsClose(${wsKey}): rejecting all deferred promises...`, + ); this.getWsStore().rejectAllDeferredPromises(wsKey, 'disconnected'); this.setWsState(wsKey, WsConnectionStateEnum.INITIAL); this.emit('close', { wsKey, event }); diff --git a/src/util/websockets/WsStore.ts b/src/util/websockets/WsStore.ts index 2744deb..6b42b2b 100644 --- a/src/util/websockets/WsStore.ts +++ b/src/util/websockets/WsStore.ts @@ -205,6 +205,9 @@ export class WsStore< const promise = this.getDeferredPromise(wsKey, promiseRef); if (promise?.reject) { + this.logger.trace( + `rejectDeferredPromise(): rejecting ${wsKey}/${promiseRef}/${value}`, + ); promise.reject(value); } From 0952e5f747fd6163912e806c2cf24741fc67aeb0 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Mon, 17 Feb 2025 11:04:33 +0000 Subject: [PATCH 23/23] feat(v4.0.0-beta.6): sync with master. Promisified subscribe requests are disabled by default. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35b92ac..1210de8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "license": "MIT", "dependencies": { "axios": "^1.7.9", diff --git a/package.json b/package.json index ae5ab4a..343e974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "4.0.0-beta.5", + "version": "4.0.0-beta.6", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts",