bitget-api
Version:
Complete Node.js & JavaScript SDK for Bitget V1-V3 REST APIs & WebSockets, with TypeScript & end-to-end tests.
227 lines • 7.33 kB
JavaScript
export const WS_LOGGER_CATEGORY = { category: 'bitget-ws' };
export const WS_BASE_URL_MAP = {
mixv1: {
all: {
livenet: 'wss://ws.bitget.com/mix/v1/stream',
demo: 'NotSupportedForV1',
},
},
spotv1: {
all: {
livenet: 'wss://ws.bitget.com/spot/v1/stream',
demo: 'NotSupportedForV1',
},
},
v2Public: {
all: {
livenet: 'wss://ws.bitget.com/v2/ws/public',
demo: 'wss://wspap.bitget.com/v2/ws/public',
},
},
v2Private: {
all: {
livenet: 'wss://ws.bitget.com/v2/ws/private',
demo: 'wss://wspap.bitget.com/v2/ws/private',
},
},
v3Public: {
all: {
livenet: 'wss://ws.bitget.com/v3/ws/public',
demo: 'wss://wspap.bitget.com/v3/ws/public',
},
},
v3Private: {
all: {
livenet: 'wss://ws.bitget.com/v3/ws/private',
demo: 'wss://wspap.bitget.com/v3/ws/private',
},
},
};
/** Should be one WS key per unique URL */
export const WS_KEY_MAP = {
spotv1: 'spotv1',
mixv1: 'mixv1',
v2Public: 'v2Public',
v2Private: 'v2Private',
v3Public: 'v3Public',
v3Private: 'v3Private',
};
/** Any WS keys in this list will trigger auth on connect, if credentials are available */
export const WS_AUTH_ON_CONNECT_KEYS = [
WS_KEY_MAP.spotv1,
WS_KEY_MAP.mixv1,
WS_KEY_MAP.v2Private,
WS_KEY_MAP.v3Private,
];
/** Any WS keys in this list will ALWAYS skip the authentication process, even if credentials are available */
export const PUBLIC_WS_KEYS = [];
/**
* Used to automatically determine if a sub request should be to a public or private ws (when there's two separate connections).
* Unnecessary if there's only one connection to handle both public & private topics.
*/
export const PRIVATE_TOPICS = ['account', 'orders', 'positions', 'ordersAlgo'];
export const PRIVATE_TOPICS_V2 = [
'account',
'orders',
'positions',
'orders-algo',
'positions-history',
'orders-crossed',
'account-crossed',
'account-isolated',
'orders-isolated',
];
export const PRIVATE_TOPICS_V3 = [
'account',
'position',
'fill',
'order',
];
export async function getWsUrl(wsKey, options, logger) {
if (options.wsUrl) {
return options.wsUrl;
}
const isDemoTrading = options.demoTrading;
const networkKey = isDemoTrading ? 'demo' : 'livenet';
switch (wsKey) {
case WS_KEY_MAP.spotv1:
case WS_KEY_MAP.mixv1: {
throw new Error('Use the WebsocketClient instead of WebsocketClientV2 for V1 websockets');
}
case WS_KEY_MAP.v2Private: {
return WS_BASE_URL_MAP.v2Private.all[networkKey];
}
case WS_KEY_MAP.v2Public: {
return WS_BASE_URL_MAP.v2Public.all[networkKey];
}
case WS_KEY_MAP.v3Private: {
return WS_BASE_URL_MAP.v3Private.all[networkKey];
}
case WS_KEY_MAP.v3Public: {
return WS_BASE_URL_MAP.v3Public.all[networkKey];
}
default: {
logger.error('getWsUrl(): Unhandled wsKey: ', {
...WS_LOGGER_CATEGORY,
wsKey,
});
throw neverGuard(wsKey, 'getWsUrl(): Unhandled wsKey');
}
}
}
export function isPrivateChannel(channel) {
return (PRIVATE_TOPICS.includes(channel) ||
PRIVATE_TOPICS_V2.includes(channel) ||
PRIVATE_TOPICS_V3.includes(channel));
}
export function getWsKeyForTopicV1(subscribeEvent,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_isPrivate) {
const instType = subscribeEvent.instType.toUpperCase();
switch (instType) {
case 'SP':
case 'SPBL': {
return WS_KEY_MAP.spotv1;
}
case 'MC':
case 'UMCBL':
case 'DMCBL': {
return WS_KEY_MAP.mixv1;
}
default: {
throw neverGuard(instType, `getWsKeyForTopicV1(): Unhandled market ${'instrumentId'}`);
}
}
}
export function getWsKeyForTopicV2(subscribeEvent, isPrivate) {
return isPrivate || isPrivateChannel(subscribeEvent.channel)
? WS_KEY_MAP.v2Private
: WS_KEY_MAP.v2Public;
}
/** Force subscription requests to be sent in smaller batches, if a number is returned */
export function getMaxTopicsPerSubscribeEvent(wsKey) {
switch (wsKey) {
case 'mixv1':
case 'spotv1':
case 'v2Public':
case 'v2Private':
case 'v3Public':
case 'v3Private': {
// Technically there doesn't seem to be a documented cap, but there is a size limit per request. Doesn't hurt to batch requests.
return 15;
}
default: {
throw neverGuard(wsKey, 'getWsKeyForTopic(): Unhandled wsKey');
}
}
}
export const WS_ERROR_ENUM = {
INVALID_ACCESS_KEY: 30011,
};
export function neverGuard(x, msg) {
return new Error(`Unhandled value exception "${x}", ${msg}`);
}
/**
* #305: ws.terminate() is undefined in browsers.
* This only works in node.js, not in browsers.
* Does nothing if `ws` is undefined. Does nothing in browsers.
*/
export function safeTerminateWs(ws, fallbackToClose) {
if (!ws) {
return false;
}
if (typeof ws['terminate'] === 'function') {
ws.terminate();
return true;
}
else if (fallbackToClose) {
ws.close();
}
return false;
}
/**
* Users can conveniently pass topics as strings or objects (object has topic name + optional params).
*
* This method normalises topics into objects (object has topic name + optional params).
*/
export function getNormalisedTopicRequests(wsTopicRequests) {
const normalisedTopicRequests = [];
for (const wsTopicRequest of wsTopicRequests) {
// passed as string, convert to object
if (typeof wsTopicRequest === 'string') {
const topicRequest = {
topic: wsTopicRequest,
payload: undefined,
};
normalisedTopicRequests.push(topicRequest);
continue;
}
// already a normalised object, thanks to user
normalisedTopicRequests.push(wsTopicRequest);
}
return normalisedTopicRequests;
}
/**
* WebSocket.ping() is not available in browsers. This is a simple check used to
* disable heartbeats in browers, for exchanges that use native WebSocket ping/pong frames.
*/
export function isWSPingFrameAvailable() {
return typeof WebSocket.prototype['ping'] === 'function';
}
/**
* WebSocket.pong() is not available in browsers. This is a simple check used to
* disable heartbeats in browers, for exchanges that use native WebSocket ping/pong frames.
*/
export function isWSPongFrameAvailable() {
return typeof WebSocket.prototype['pong'] === 'function';
}
/**
* WS API promises are stored using a primary key. This key is constructed using
* properties found in every request & reply.
*/
export function getPromiseRefForWSAPIRequest(requestEvent) {
// Responses don't have any other info we can use to connect them to the request. Just the "id" field...
const promiseRef = [requestEvent.id].join('_');
return promiseRef;
}
//# sourceMappingURL=websocket-util.js.map