diff --git a/src/types/shared.ts b/src/types/shared.ts index 3d70cff..4a1f2ec 100644 --- a/src/types/shared.ts +++ b/src/types/shared.ts @@ -1,3 +1,4 @@ +import { ContractClient } from '../contract-client'; import { InverseClient } from '../inverse-client'; import { LinearClient } from '../linear-client'; import { SpotClient } from '../spot-client'; @@ -13,7 +14,8 @@ export type RESTClient = | SpotClientV3 | USDCOptionClient | USDCPerpetualClient - | UnifiedMarginClient; + | UnifiedMarginClient + | ContractClient; export type numberInString = string; diff --git a/src/types/websockets.ts b/src/types/websockets.ts index 68b100f..6354c39 100644 --- a/src/types/websockets.ts +++ b/src/types/websockets.ts @@ -9,7 +9,9 @@ export type APIMarket = | 'usdcOption' | 'usdcPerp' | 'unifiedPerp' - | 'unifiedOption'; + | 'unifiedOption' + | 'contractUSDT' + | 'contractInverse'; // Same as inverse futures export type WsPublicInverseTopic = diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index d8dc899..fa309f5 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -121,6 +121,26 @@ export const WS_BASE_URL_MAP: Record< testnet: 'useUnifiedEndpoint', }, }, + contractUSDT: { + public: { + livenet: 'wss://stream.bybit.com/contract/usdt/public/v3', + testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3', + }, + private: { + livenet: 'wss://stream.bybit.com/contract/private/v3', + testnet: 'wss://stream-testnet.bybit.com/contract/private/v3', + }, + }, + contractInverse: { + public: { + livenet: 'wss://stream.bybit.com/contract/inverse/public/v3', + testnet: 'wss://stream-testnet.bybit.com/contract/inverse/public/v3', + }, + private: { + livenet: 'wss://stream.bybit.com/contract/private/v3', + testnet: 'wss://stream-testnet.bybit.com/contract/private/v3', + }, + }, }; export const WS_KEY_MAP = { @@ -139,6 +159,10 @@ export const WS_KEY_MAP = { unifiedOptionPublic: 'unifiedOptionPublic', unifiedPerpUSDTPublic: 'unifiedPerpUSDTPublic', unifiedPerpUSDCPublic: 'unifiedPerpUSDCPublic', + contractUSDTPublic: 'contractUSDTPublic', + contractUSDTPrivate: 'contractUSDTPrivate', + contractInversePublic: 'contractInversePublic', + contractInversePrivate: 'contractInversePrivate', } as const; export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [ @@ -146,6 +170,8 @@ export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [ WS_KEY_MAP.usdcOptionPrivate, WS_KEY_MAP.usdcPerpPrivate, WS_KEY_MAP.unifiedPrivate, + WS_KEY_MAP.contractUSDTPrivate, + WS_KEY_MAP.contractInversePrivate, ]; export const PUBLIC_WS_KEYS = [ @@ -157,6 +183,8 @@ export const PUBLIC_WS_KEYS = [ WS_KEY_MAP.unifiedOptionPublic, WS_KEY_MAP.unifiedPerpUSDTPublic, WS_KEY_MAP.unifiedPerpUSDCPublic, + WS_KEY_MAP.contractUSDTPublic, + WS_KEY_MAP.contractInversePublic, ] as string[]; /** Used to automatically determine if a sub request should be to the public or private ws (when there's two) */ @@ -251,6 +279,16 @@ export function getWsKeyForTopic( `Failed to determine wskey for unified perps topic: "${topic}` ); } + case 'contractInverse': { + return isPrivateTopic + ? WS_KEY_MAP.contractInversePrivate + : WS_KEY_MAP.contractInversePublic; + } + case 'contractUSDT': { + return isPrivateTopic + ? WS_KEY_MAP.contractUSDTPrivate + : WS_KEY_MAP.contractUSDTPublic; + } default: { throw neverGuard(market, `getWsKeyForTopic(): Unhandled market`); } @@ -267,7 +305,9 @@ export function getMaxTopicsPerSubscribeEvent( case 'usdcPerp': case 'unifiedOption': case 'unifiedPerp': - case 'spot': { + case 'spot': + case 'contractInverse': + case 'contractUSDT': { return null; } case 'spotv3': { diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 0a2bef7..6292df1 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -35,6 +35,7 @@ import { import { USDCOptionClient } from './usdc-option-client'; import { USDCPerpetualClient } from './usdc-perpetual-client'; import { UnifiedMarginClient } from './unified-margin-client'; +import { ContractClient } from './contract-client'; const loggerCategory = { category: 'bybit-ws' }; @@ -232,6 +233,14 @@ export class WebsocketClient extends EventEmitter { ); break; } + case 'contractInverse': + case 'contractUSDT': { + this.restClient = new ContractClient( + this.options.restOptions, + this.options.requestOptions + ); + break; + } default: { throw neverGuard( this.options.market, @@ -285,7 +294,9 @@ export class WebsocketClient extends EventEmitter { case 'usdcOption': case 'usdcPerp': case 'unifiedPerp': - case 'unifiedOption': { + case 'unifiedOption': + case 'contractUSDT': + case 'contractInverse': { return [...this.connectPublic(), this.connectPrivate()]; } default: { @@ -323,6 +334,10 @@ export class WebsocketClient extends EventEmitter { this.connect(WS_KEY_MAP.unifiedPerpUSDCPublic), ]; } + case 'contractUSDT': + return [this.connect(WS_KEY_MAP.contractUSDTPublic)]; + case 'contractInverse': + return [this.connect(WS_KEY_MAP.contractInversePublic)]; default: { throw neverGuard( this.options.market, @@ -356,6 +371,10 @@ export class WebsocketClient extends EventEmitter { case 'unifiedOption': { return this.connect(WS_KEY_MAP.unifiedPrivate); } + case 'contractUSDT': + return this.connect(WS_KEY_MAP.contractUSDTPrivate); + case 'contractInverse': + return this.connect(WS_KEY_MAP.contractInversePrivate); default: { throw neverGuard( this.options.market, @@ -899,6 +918,18 @@ export class WebsocketClient extends EventEmitter { case WS_KEY_MAP.unifiedPrivate: { return WS_BASE_URL_MAP.unifiedPerp.private[networkKey]; } + case WS_KEY_MAP.contractInversePrivate: { + return WS_BASE_URL_MAP.contractInverse.private[networkKey]; + } + case WS_KEY_MAP.contractInversePublic: { + return WS_BASE_URL_MAP.contractInverse.public[networkKey]; + } + case WS_KEY_MAP.contractUSDTPrivate: { + return WS_BASE_URL_MAP.contractUSDT.private[networkKey]; + } + case WS_KEY_MAP.contractUSDTPublic: { + return WS_BASE_URL_MAP.contractUSDT.public[networkKey]; + } default: { this.logger.error('getWsUrl(): Unhandled wsKey: ', { ...loggerCategory,