v3.5.1: feat() add support for V5 public & private websockets
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bybit-api",
|
||||
"version": "3.5.0-beta.0",
|
||||
"version": "3.5.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bybit-api",
|
||||
"version": "3.5.0-beta.0",
|
||||
"version": "3.5.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bybit-api",
|
||||
"version": "3.5.0",
|
||||
"version": "3.5.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",
|
||||
|
||||
@@ -1,6 +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';
|
||||
@@ -15,7 +16,8 @@ export type RESTClient =
|
||||
| USDCOptionClient
|
||||
| USDCPerpetualClient
|
||||
| UnifiedMarginClient
|
||||
| ContractClient;
|
||||
| ContractClient
|
||||
| RestClientV5;
|
||||
|
||||
export type numberInString = string;
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ export type APIMarket =
|
||||
| 'unifiedPerp'
|
||||
| 'unifiedOption'
|
||||
| 'contractUSDT'
|
||||
| 'contractInverse';
|
||||
| 'contractInverse'
|
||||
| 'v5';
|
||||
|
||||
// Same as inverse futures
|
||||
export type WsPublicInverseTopic =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { APIMarket, WsKey } from '../types';
|
||||
import { APIMarket, CategoryV5, WsKey } from '../types';
|
||||
|
||||
interface NetworkMapV3 {
|
||||
livenet: string;
|
||||
@@ -9,10 +9,28 @@ interface NetworkMapV3 {
|
||||
|
||||
type PublicPrivateNetwork = 'public' | 'private';
|
||||
|
||||
/**
|
||||
* The following WS keys are logical.
|
||||
*
|
||||
* They're not directly used as a market. They usually have one private endpoint but many public ones,
|
||||
* so they need a bit of extra handling for seamless messaging between endpoints.
|
||||
*
|
||||
* For the unified keys, the "split" happens using the symbol. Symbols suffixed with USDT are obviously USDT topics.
|
||||
* For the v5 endpoints, the subscribe/unsubscribe call must specify the category the subscription should route to.
|
||||
*/
|
||||
type PublicOnlyWsKeys =
|
||||
| 'unifiedPerpUSDT'
|
||||
| 'unifiedPerpUSDC'
|
||||
| 'v5SpotPublic'
|
||||
| 'v5LinearPublic'
|
||||
| 'v5InversePublic'
|
||||
| 'v5OptionPublic';
|
||||
|
||||
export const WS_BASE_URL_MAP: Record<
|
||||
APIMarket | 'unifiedPerpUSDT' | 'unifiedPerpUSDC',
|
||||
APIMarket,
|
||||
Record<PublicPrivateNetwork, NetworkMapV3>
|
||||
> = {
|
||||
> &
|
||||
Record<PublicOnlyWsKeys, Record<'public', NetworkMapV3>> = {
|
||||
inverse: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/realtime',
|
||||
@@ -106,20 +124,12 @@ export const WS_BASE_URL_MAP: Record<
|
||||
livenet: 'wss://stream.bybit.com/contract/usdt/public/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3',
|
||||
},
|
||||
private: {
|
||||
livenet: 'useUnifiedEndpoint',
|
||||
testnet: 'useUnifiedEndpoint',
|
||||
},
|
||||
},
|
||||
unifiedPerpUSDC: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/contract/usdc/public/v3',
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/usdc/public/v3',
|
||||
},
|
||||
private: {
|
||||
livenet: 'useUnifiedEndpoint',
|
||||
testnet: 'useUnifiedEndpoint',
|
||||
},
|
||||
},
|
||||
contractUSDT: {
|
||||
public: {
|
||||
@@ -141,6 +151,40 @@ export const WS_BASE_URL_MAP: Record<
|
||||
testnet: 'wss://stream-testnet.bybit.com/contract/private/v3',
|
||||
},
|
||||
},
|
||||
v5: {
|
||||
public: {
|
||||
livenet: 'public topics are routed internally via the public wskeys',
|
||||
testnet: 'public topics are routed internally via the public wskeys',
|
||||
},
|
||||
private: {
|
||||
livenet: 'wss://stream.bybit.com/v5/private',
|
||||
testnet: 'wss://stream-testnet.bybit.com/v5/private',
|
||||
},
|
||||
},
|
||||
v5SpotPublic: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/v5/public/spot',
|
||||
testnet: 'wss://stream-testnet.bybit.com/v5/public/spot',
|
||||
},
|
||||
},
|
||||
v5LinearPublic: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/v5/public/linear',
|
||||
testnet: 'wss://stream-testnet.bybit.com/v5/public/linear',
|
||||
},
|
||||
},
|
||||
v5InversePublic: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/v5/public/inverse',
|
||||
testnet: 'wss://stream-testnet.bybit.com/v5/public/inverse',
|
||||
},
|
||||
},
|
||||
v5OptionPublic: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/v5/public/option',
|
||||
testnet: 'wss://stream-testnet.bybit.com/v5/public/option',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WS_KEY_MAP = {
|
||||
@@ -163,6 +207,11 @@ export const WS_KEY_MAP = {
|
||||
contractUSDTPrivate: 'contractUSDTPrivate',
|
||||
contractInversePublic: 'contractInversePublic',
|
||||
contractInversePrivate: 'contractInversePrivate',
|
||||
v5SpotPublic: 'v5SpotPublic',
|
||||
v5LinearPublic: 'v5LinearPublic',
|
||||
v5InversePublic: 'v5InversePublic',
|
||||
v5OptionPublic: 'v5OptionPublic',
|
||||
v5Private: 'v5Private',
|
||||
} as const;
|
||||
|
||||
export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
|
||||
@@ -172,6 +221,7 @@ export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
|
||||
WS_KEY_MAP.unifiedPrivate,
|
||||
WS_KEY_MAP.contractUSDTPrivate,
|
||||
WS_KEY_MAP.contractInversePrivate,
|
||||
WS_KEY_MAP.v5Private,
|
||||
];
|
||||
|
||||
export const PUBLIC_WS_KEYS = [
|
||||
@@ -185,15 +235,15 @@ export const PUBLIC_WS_KEYS = [
|
||||
WS_KEY_MAP.unifiedPerpUSDCPublic,
|
||||
WS_KEY_MAP.contractUSDTPublic,
|
||||
WS_KEY_MAP.contractInversePublic,
|
||||
WS_KEY_MAP.v5SpotPublic,
|
||||
WS_KEY_MAP.v5LinearPublic,
|
||||
WS_KEY_MAP.v5InversePublic,
|
||||
WS_KEY_MAP.v5OptionPublic,
|
||||
] as string[];
|
||||
|
||||
/** Used to automatically determine if a sub request should be to the public or private ws (when there's two) */
|
||||
const PRIVATE_TOPICS = [
|
||||
'position',
|
||||
'execution',
|
||||
'order',
|
||||
'stop_order',
|
||||
'wallet',
|
||||
'outboundAccountInfo',
|
||||
'executionReport',
|
||||
'ticketInfo',
|
||||
@@ -226,12 +276,23 @@ const PRIVATE_TOPICS = [
|
||||
'user.execution.contractAccount',
|
||||
'user.order.contractAccount',
|
||||
'user.wallet.contractAccount',
|
||||
// v5
|
||||
'position',
|
||||
'execution',
|
||||
'order',
|
||||
'wallet',
|
||||
'greeks',
|
||||
];
|
||||
|
||||
export function isPrivateWsTopic(topic: string): boolean {
|
||||
return PRIVATE_TOPICS.includes(topic);
|
||||
}
|
||||
|
||||
export function getWsKeyForTopic(
|
||||
market: APIMarket,
|
||||
topic: string,
|
||||
isPrivate?: boolean
|
||||
isPrivate?: boolean,
|
||||
category?: CategoryV5
|
||||
): WsKey {
|
||||
const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic);
|
||||
switch (market) {
|
||||
@@ -297,12 +358,138 @@ export function getWsKeyForTopic(
|
||||
? WS_KEY_MAP.contractUSDTPrivate
|
||||
: WS_KEY_MAP.contractUSDTPublic;
|
||||
}
|
||||
case 'v5': {
|
||||
if (isPrivateTopic) {
|
||||
return WS_KEY_MAP.v5Private;
|
||||
}
|
||||
|
||||
switch (category) {
|
||||
case 'spot': {
|
||||
return WS_KEY_MAP.v5SpotPublic;
|
||||
}
|
||||
case 'linear': {
|
||||
return WS_KEY_MAP.v5LinearPublic;
|
||||
}
|
||||
case 'inverse': {
|
||||
return WS_KEY_MAP.v5InversePublic;
|
||||
}
|
||||
case 'option': {
|
||||
return WS_KEY_MAP.v5OptionPublic;
|
||||
}
|
||||
case undefined: {
|
||||
throw new Error('Category cannot be undefined');
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
category,
|
||||
'getWsKeyForTopic(v5): Unhandled v5 category'
|
||||
);
|
||||
}
|
||||
}
|
||||
// TODO: simple way to manage many public api groups in one api market?
|
||||
return isPrivateTopic ? WS_KEY_MAP.v5Private : WS_KEY_MAP.v5Private;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(market, 'getWsKeyForTopic(): Unhandled market');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getWsUrl(
|
||||
wsKey: WsKey,
|
||||
wsUrl: string | undefined,
|
||||
isTestnet: boolean
|
||||
): string {
|
||||
if (wsUrl) {
|
||||
return wsUrl;
|
||||
}
|
||||
|
||||
const networkKey = isTestnet ? 'testnet' : 'livenet';
|
||||
|
||||
switch (wsKey) {
|
||||
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];
|
||||
}
|
||||
case WS_KEY_MAP.v5Private: {
|
||||
return WS_BASE_URL_MAP.v5.private[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.v5SpotPublic: {
|
||||
return WS_BASE_URL_MAP.v5SpotPublic.public[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.v5LinearPublic: {
|
||||
return WS_BASE_URL_MAP.v5LinearPublic.public[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.v5InversePublic: {
|
||||
return WS_BASE_URL_MAP.v5InversePublic.public[networkKey];
|
||||
}
|
||||
case WS_KEY_MAP.v5OptionPublic: {
|
||||
return WS_BASE_URL_MAP.v5OptionPublic.public[networkKey];
|
||||
}
|
||||
default: {
|
||||
this.logger.error('getWsUrl(): Unhandled wsKey: ', {
|
||||
category: 'bybit-ws',
|
||||
wsKey,
|
||||
});
|
||||
throw neverGuard(wsKey, 'getWsUrl(): Unhandled wsKey');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getMaxTopicsPerSubscribeEvent(
|
||||
market: APIMarket
|
||||
): number | null {
|
||||
@@ -315,7 +502,8 @@ export function getMaxTopicsPerSubscribeEvent(
|
||||
case 'unifiedPerp':
|
||||
case 'spot':
|
||||
case 'contractInverse':
|
||||
case 'contractUSDT': {
|
||||
case 'contractUSDT':
|
||||
case 'v5': {
|
||||
return null;
|
||||
}
|
||||
case 'spotv3': {
|
||||
|
||||
@@ -17,6 +17,7 @@ import WsStore from './util/WsStore';
|
||||
|
||||
import {
|
||||
APIMarket,
|
||||
CategoryV5,
|
||||
KlineInterval,
|
||||
RESTClient,
|
||||
WSClientConfigurableOptions,
|
||||
@@ -34,10 +35,13 @@ import {
|
||||
WsConnectionStateEnum,
|
||||
getMaxTopicsPerSubscribeEvent,
|
||||
getWsKeyForTopic,
|
||||
getWsUrl,
|
||||
isPrivateWsTopic,
|
||||
isWsPong,
|
||||
neverGuard,
|
||||
serializeParams,
|
||||
} from './util';
|
||||
import { RestClientV5 } from './rest-client-v5';
|
||||
|
||||
const loggerCategory = { category: 'bybit-ws' };
|
||||
|
||||
@@ -119,13 +123,82 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.on('error', () => {});
|
||||
}
|
||||
|
||||
/** Get the WsStore that tracks websockets & topics */
|
||||
public getWsStore(): WsStore {
|
||||
return this.wsStore;
|
||||
}
|
||||
|
||||
public isTestnet(): boolean {
|
||||
return this.options.testnet === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to topics & track/persist them. They will be automatically resubscribed to if the connection drops/reconnects.
|
||||
* @param wsTopics topic or list of topics
|
||||
* 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)
|
||||
*/
|
||||
public subscribeV5(
|
||||
wsTopics: WsTopic[] | WsTopic[],
|
||||
category: CategoryV5,
|
||||
isPrivateTopic?: boolean
|
||||
) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
|
||||
topics.forEach((topic) => {
|
||||
const wsKey = getWsKeyForTopic(
|
||||
this.options.market,
|
||||
topic,
|
||||
isPrivateTopic,
|
||||
category
|
||||
);
|
||||
|
||||
// Persist topic for reconnects
|
||||
this.wsStore.addTopic(wsKey, topic);
|
||||
|
||||
// if connected, send subscription request
|
||||
if (
|
||||
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED)
|
||||
) {
|
||||
return this.requestSubscribeTopics(wsKey, [topic]);
|
||||
}
|
||||
|
||||
// start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect
|
||||
if (
|
||||
!this.wsStore.isConnectionState(
|
||||
wsKey,
|
||||
WsConnectionStateEnum.CONNECTING
|
||||
) &&
|
||||
!this.wsStore.isConnectionState(
|
||||
wsKey,
|
||||
WsConnectionStateEnum.RECONNECTING
|
||||
)
|
||||
) {
|
||||
return this.connect(wsKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to V1-V3 topics & track/persist them.
|
||||
*
|
||||
* Note: for public V5 topics use the `subscribeV5()` method.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
public subscribe(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 subscribeV5() method & provide the category parameter'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
topics.forEach((topic) => {
|
||||
const wsKey = getWsKeyForTopic(
|
||||
@@ -161,12 +234,57 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from 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)
|
||||
*/
|
||||
public unsubscribeV5(
|
||||
wsTopics: WsTopic[] | WsTopic[],
|
||||
category: CategoryV5,
|
||||
isPrivateTopic?: boolean
|
||||
) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
topics.forEach((topic) => {
|
||||
const wsKey = getWsKeyForTopic(
|
||||
this.options.market,
|
||||
topic,
|
||||
isPrivateTopic,
|
||||
category
|
||||
);
|
||||
|
||||
// Remove topic from persistence for reconnects
|
||||
this.wsStore.deleteTopic(wsKey, topic);
|
||||
|
||||
// unsubscribe request only necessary if active connection exists
|
||||
if (
|
||||
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED)
|
||||
) {
|
||||
this.requestUnsubscribeTopics(wsKey, [topic]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from V1-V3 topics & remove them from memory. They won't be re-subscribed to if the connection reconnects.
|
||||
*
|
||||
* 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 unsubscribe(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,
|
||||
@@ -251,6 +369,13 @@ export class WebsocketClient extends EventEmitter {
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'v5': {
|
||||
this.restClient = new RestClientV5(
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -260,15 +385,6 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the WsStore that tracks websockets & topics */
|
||||
public getWsStore(): WsStore {
|
||||
return this.wsStore;
|
||||
}
|
||||
|
||||
public isTestnet(): boolean {
|
||||
return this.options.testnet === true;
|
||||
}
|
||||
|
||||
public close(wsKey: WsKey, force?: boolean) {
|
||||
this.logger.info('Closing connection', { ...loggerCategory, wsKey });
|
||||
this.setWsState(wsKey, WsConnectionStateEnum.CLOSING);
|
||||
@@ -310,6 +426,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'contractInverse': {
|
||||
return [...this.connectPublic(), this.connectPrivate()];
|
||||
}
|
||||
case 'v5': {
|
||||
return [this.connectPrivate()];
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(this.options.market, 'connectAll(): Unhandled market');
|
||||
}
|
||||
@@ -349,6 +468,14 @@ export class WebsocketClient extends EventEmitter {
|
||||
return [this.connect(WS_KEY_MAP.contractUSDTPublic)];
|
||||
case 'contractInverse':
|
||||
return [this.connect(WS_KEY_MAP.contractInversePublic)];
|
||||
case 'v5': {
|
||||
return [
|
||||
this.connect(WS_KEY_MAP.v5SpotPublic),
|
||||
this.connect(WS_KEY_MAP.v5LinearPublic),
|
||||
this.connect(WS_KEY_MAP.v5InversePublic),
|
||||
this.connect(WS_KEY_MAP.v5OptionPublic),
|
||||
];
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -386,6 +513,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
return this.connect(WS_KEY_MAP.contractUSDTPrivate);
|
||||
case 'contractInverse':
|
||||
return this.connect(WS_KEY_MAP.contractInversePrivate);
|
||||
case 'v5': {
|
||||
return this.connect(WS_KEY_MAP.v5Private);
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -423,8 +553,8 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
const authParams = await this.getAuthParams(wsKey);
|
||||
const url = this.getWsUrl(wsKey) + authParams;
|
||||
const ws = this.connectToWsUrl(url, wsKey);
|
||||
const url = getWsUrl(wsKey, this.options.wsUrl, this.isTestnet());
|
||||
const ws = this.connectToWsUrl(url + authParams, wsKey);
|
||||
|
||||
return this.wsStore.setWs(wsKey, ws);
|
||||
} catch (err) {
|
||||
@@ -891,85 +1021,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.wsStore.setConnectionState(wsKey, state);
|
||||
}
|
||||
|
||||
private getWsUrl(wsKey: WsKey): string {
|
||||
if (this.options.wsUrl) {
|
||||
return this.options.wsUrl;
|
||||
}
|
||||
|
||||
const networkKey = this.isTestnet() ? 'testnet' : 'livenet';
|
||||
|
||||
switch (wsKey) {
|
||||
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: {
|
||||
this.logger.error('getWsUrl(): Unhandled wsKey: ', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
throw neverGuard(wsKey, 'getWsUrl(): Unhandled wsKey');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private wrongMarketError(market: APIMarket) {
|
||||
return new Error(
|
||||
`This WS client was instanced for the ${this.options.market} market. Make another WebsocketClient instance with "market: '${market}' to listen to spot topics`
|
||||
`This WS client was instanced for the ${this.options.market} market. Make another WebsocketClient instance with "market: '${market}'" to listen to ${market} topics`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user