bybit-api-gnome
Version:
Forked for Lick Hunter, Complete & robust node.js SDK for Bybit's REST APIs and WebSockets v5, with TypeScript & integration tests.
791 lines • 33.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebsocketClient = void 0;
const events_1 = require("events");
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
const inverse_client_1 = require("./inverse-client");
const linear_client_1 = require("./linear-client");
const spot_client_v3_1 = require("./spot-client-v3");
const spot_client_1 = require("./spot-client");
const usdc_option_client_1 = require("./usdc-option-client");
const usdc_perpetual_client_1 = require("./usdc-perpetual-client");
const unified_margin_client_1 = require("./unified-margin-client");
const contract_client_1 = require("./contract-client");
const node_support_1 = require("./util/node-support");
const WsStore_1 = __importDefault(require("./util/WsStore"));
const util_1 = require("./util");
const loggerCategory = { category: 'bybit-ws' };
class WebsocketClient extends events_1.EventEmitter {
constructor(options, logger) {
super();
this.logger = logger || util_1.DefaultLogger;
this.wsStore = new WsStore_1.default(this.logger);
this.options = {
testnet: false,
pongTimeout: 1000,
pingInterval: 10000,
reconnectTimeout: 500,
fetchTimeOffsetBeforeAuth: false,
...options,
};
this.options.restOptions = {
...this.options.restOptions,
testnet: this.options.testnet,
};
this.prepareRESTClient();
// add default error handling so this doesn't crash node (if the user didn't set a handler)
this.on('error', () => { });
}
/**
* Subscribe to topics & track/persist them. They will be automatically resubscribed to if the connection 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)
*/
subscribe(wsTopics, isPrivateTopic) {
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
topics.forEach((topic) => {
const wsKey = (0, util_1.getWsKeyForTopic)(this.options.market, topic, isPrivateTopic);
// Persist topic for reconnects
this.wsStore.addTopic(wsKey, topic);
// if connected, send subscription request
if (this.wsStore.isConnectionState(wsKey, util_1.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, util_1.WsConnectionStateEnum.CONNECTING) &&
!this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.RECONNECTING)) {
return this.connect(wsKey);
}
});
}
/**
* Unsubscribe from topics & remove them from memory. They won't be re-subscribed to if the connection 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)
*/
unsubscribe(wsTopics, isPrivateTopic) {
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
topics.forEach((topic) => {
const wsKey = (0, util_1.getWsKeyForTopic)(this.options.market, topic, isPrivateTopic);
// Remove topic from persistence for reconnects
this.wsStore.deleteTopic(wsKey, topic);
// unsubscribe request only necessary if active connection exists
if (this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.CONNECTED)) {
this.requestUnsubscribeTopics(wsKey, [topic]);
}
});
}
/**
* @private Only used if we fetch exchange time before attempting auth. Disabled by default.
* I've removed this for ftx and it's working great, tempted to remove this here
*/
prepareRESTClient() {
switch (this.options.market) {
case 'inverse': {
this.restClient = new inverse_client_1.InverseClient(this.options.restOptions, this.options.requestOptions);
break;
}
case 'linear': {
this.restClient = new linear_client_1.LinearClient(this.options.restOptions, this.options.requestOptions);
break;
}
case 'spot': {
this.restClient = new spot_client_1.SpotClient(this.options.restOptions, this.options.requestOptions);
this.connectPublic();
break;
}
case 'spotv3': {
this.restClient = new spot_client_v3_1.SpotClientV3(this.options.restOptions, this.options.requestOptions);
break;
}
case 'usdcOption': {
this.restClient = new usdc_option_client_1.USDCOptionClient(this.options.restOptions, this.options.requestOptions);
break;
}
case 'usdcPerp': {
this.restClient = new usdc_perpetual_client_1.USDCPerpetualClient(this.options.restOptions, this.options.requestOptions);
break;
}
case 'unifiedOption':
case 'unifiedPerp': {
this.restClient = new unified_margin_client_1.UnifiedMarginClient(this.options.restOptions, this.options.requestOptions);
break;
}
case 'contractInverse':
case 'contractUSDT': {
this.restClient = new contract_client_1.ContractClient(this.options.restOptions, this.options.requestOptions);
break;
}
default: {
throw (0, util_1.neverGuard)(this.options.market, `prepareRESTClient(): Unhandled market`);
}
}
}
/** Get the WsStore that tracks websockets & topics */
getWsStore() {
return this.wsStore;
}
isTestnet() {
return this.options.testnet === true;
}
close(wsKey, force) {
this.logger.info('Closing connection', { ...loggerCategory, wsKey });
this.setWsState(wsKey, util_1.WsConnectionStateEnum.CLOSING);
this.clearTimers(wsKey);
const ws = this.getWs(wsKey);
ws === null || ws === void 0 ? void 0 : ws.close();
if (force) {
ws === null || ws === void 0 ? void 0 : ws.terminate();
}
}
closeAll(force) {
const keys = this.wsStore.getKeys();
this.logger.info(`Closing all ws connections: ${keys}`);
keys.forEach((key) => {
this.close(key, force);
});
}
/**
* Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library
*/
connectAll() {
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()];
}
default: {
throw (0, util_1.neverGuard)(this.options.market, `connectAll(): Unhandled market`);
}
}
}
connectPublic() {
switch (this.options.market) {
case 'inverse': {
return [this.connect(util_1.WS_KEY_MAP.inverse)];
}
case 'linear': {
return [this.connect(util_1.WS_KEY_MAP.linearPublic)];
}
case 'spot': {
return [this.connect(util_1.WS_KEY_MAP.spotPublic)];
}
case 'spotv3': {
return [this.connect(util_1.WS_KEY_MAP.spotV3Public)];
}
case 'usdcOption': {
return [this.connect(util_1.WS_KEY_MAP.usdcOptionPublic)];
}
case 'usdcPerp': {
return [this.connect(util_1.WS_KEY_MAP.usdcPerpPublic)];
}
case 'unifiedOption': {
return [this.connect(util_1.WS_KEY_MAP.unifiedOptionPublic)];
}
case 'unifiedPerp': {
return [
this.connect(util_1.WS_KEY_MAP.unifiedPerpUSDTPublic),
this.connect(util_1.WS_KEY_MAP.unifiedPerpUSDCPublic),
];
}
case 'contractUSDT':
return [this.connect(util_1.WS_KEY_MAP.contractUSDTPublic)];
case 'contractInverse':
return [this.connect(util_1.WS_KEY_MAP.contractInversePublic)];
default: {
throw (0, util_1.neverGuard)(this.options.market, `connectPublic(): Unhandled market`);
}
}
}
connectPrivate() {
switch (this.options.market) {
case 'inverse': {
return this.connect(util_1.WS_KEY_MAP.inverse);
}
case 'linear': {
return this.connect(util_1.WS_KEY_MAP.linearPrivate);
}
case 'spot': {
return this.connect(util_1.WS_KEY_MAP.spotPrivate);
}
case 'spotv3': {
return this.connect(util_1.WS_KEY_MAP.spotV3Private);
}
case 'usdcOption': {
return this.connect(util_1.WS_KEY_MAP.usdcOptionPrivate);
}
case 'usdcPerp': {
return this.connect(util_1.WS_KEY_MAP.usdcPerpPrivate);
}
case 'unifiedPerp':
case 'unifiedOption': {
return this.connect(util_1.WS_KEY_MAP.unifiedPrivate);
}
case 'contractUSDT':
return this.connect(util_1.WS_KEY_MAP.contractUSDTPrivate);
case 'contractInverse':
return this.connect(util_1.WS_KEY_MAP.contractInversePrivate);
default: {
throw (0, util_1.neverGuard)(this.options.market, `connectPrivate(): Unhandled market`);
}
}
}
async connect(wsKey) {
try {
if (this.wsStore.isWsOpen(wsKey)) {
this.logger.error('Refused to connect to ws with existing active connection', { ...loggerCategory, wsKey });
return this.wsStore.getWs(wsKey);
}
if (this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.CONNECTING)) {
this.logger.error('Refused to connect to ws, connection attempt already active', { ...loggerCategory, wsKey });
return;
}
if (!this.wsStore.getConnectionState(wsKey) ||
this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.INITIAL)) {
this.setWsState(wsKey, util_1.WsConnectionStateEnum.CONNECTING);
}
const authParams = await this.getAuthParams(wsKey);
const url = this.getWsUrl(wsKey) + authParams;
const ws = this.connectToWsUrl(url, wsKey);
return this.wsStore.setWs(wsKey, ws);
}
catch (err) {
this.parseWsError('Connection failed', err, wsKey);
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
}
}
parseWsError(context, error, wsKey) {
if (!error.message) {
this.logger.error(`${context} due to unexpected error: `, error);
this.emit('error', error);
return;
}
switch (error.message) {
case 'Unexpected server response: 401':
this.logger.error(`${context} due to 401 authorization failure.`, {
...loggerCategory,
wsKey,
});
break;
default:
if (this.wsStore.getConnectionState(wsKey) !==
util_1.WsConnectionStateEnum.CLOSING) {
this.logger.error(`${context} due to unexpected response error: "${(error === null || error === void 0 ? void 0 : error.msg) || (error === null || error === void 0 ? void 0 : error.message) || error}"`, { ...loggerCategory, wsKey, error });
this.executeReconnectableClose(wsKey, 'unhandled onWsError');
}
else {
this.logger.info(`${wsKey} socket forcefully closed. Will not reconnect.`);
}
break;
}
this.emit('error', error);
}
/**
* Return params required to make authorized request
*/
async getAuthParams(wsKey) {
if (util_1.PUBLIC_WS_KEYS.includes(wsKey)) {
this.logger.debug('Starting public only websocket client.', {
...loggerCategory,
wsKey,
});
return '';
}
try {
const { signature, expiresAt } = await this.getWsAuthSignature(wsKey);
const authParams = {
api_key: this.options.key,
expires: expiresAt,
signature,
};
return '?' + (0, util_1.serializeParams)(authParams);
}
catch (e) {
this.logger.error(e, { ...loggerCategory, wsKey });
return '';
}
}
async sendAuthRequest(wsKey) {
try {
const { signature, expiresAt } = await this.getWsAuthSignature(wsKey);
const request = {
op: 'auth',
args: [this.options.key, expiresAt, signature],
req_id: `${wsKey}-auth`,
};
return this.tryWsSend(wsKey, JSON.stringify(request));
}
catch (e) {
this.logger.error(e, { ...loggerCategory, wsKey });
}
}
async getWsAuthSignature(wsKey) {
var _a;
const { key, secret } = this.options;
if (!key || !secret) {
this.logger.warning('Cannot authenticate websocket, either api or private keys missing.', { ...loggerCategory, wsKey });
throw new Error(`Cannot auth - missing api or secret in config`);
}
this.logger.debug("Getting auth'd request params", {
...loggerCategory,
wsKey,
});
const timeOffset = this.options.fetchTimeOffsetBeforeAuth
? (await ((_a = this.restClient) === null || _a === void 0 ? void 0 : _a.fetchTimeOffset())) || 0
: 0;
const signatureExpiresAt = Date.now() + timeOffset + 5000;
const signature = await (0, node_support_1.signMessage)('GET/realtime' + signatureExpiresAt, secret);
return {
expiresAt: signatureExpiresAt,
signature,
};
}
reconnectWithDelay(wsKey, connectionDelayMs) {
var _a;
this.clearTimers(wsKey);
if (this.wsStore.getConnectionState(wsKey) !==
util_1.WsConnectionStateEnum.CONNECTING) {
this.setWsState(wsKey, util_1.WsConnectionStateEnum.RECONNECTING);
}
if ((_a = this.wsStore.get(wsKey)) === null || _a === void 0 ? void 0 : _a.activeReconnectTimer) {
this.clearReconnectTimer(wsKey);
}
this.wsStore.get(wsKey, true).activeReconnectTimer = setTimeout(() => {
this.logger.info('Reconnecting to websocket', {
...loggerCategory,
wsKey,
});
this.clearReconnectTimer(wsKey);
this.connect(wsKey);
}, connectionDelayMs);
}
ping(wsKey) {
if (this.wsStore.get(wsKey, true).activePongTimer) {
return;
}
this.clearPongTimer(wsKey);
this.logger.silly('Sending ping', { ...loggerCategory, wsKey });
this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' }));
this.wsStore.get(wsKey, true).activePongTimer = setTimeout(() => this.executeReconnectableClose(wsKey, 'Pong timeout'), this.options.pongTimeout);
}
/**
* Closes a connection, if it's even open. If open, this will trigger a reconnect asynchronously.
* If closed, trigger a reconnect immediately
*/
executeReconnectableClose(wsKey, reason) {
var _a;
this.logger.info(`${reason} - closing socket to reconnect`, {
...loggerCategory,
wsKey,
reason,
});
const wasOpen = this.wsStore.isWsOpen(wsKey);
(_a = this.getWs(wsKey)) === null || _a === void 0 ? void 0 : _a.terminate();
delete this.wsStore.get(wsKey, true).activePongTimer;
this.clearPingTimer(wsKey);
this.clearPongTimer(wsKey);
if (!wasOpen) {
this.logger.info(`${reason} - socket already closed - trigger immediate reconnect`, {
...loggerCategory,
wsKey,
reason,
});
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
}
}
clearTimers(wsKey) {
this.clearPingTimer(wsKey);
this.clearPongTimer(wsKey);
this.clearReconnectTimer(wsKey);
}
// Send a ping at intervals
clearPingTimer(wsKey) {
const wsState = this.wsStore.get(wsKey);
if (wsState === null || wsState === void 0 ? void 0 : wsState.activePingTimer) {
clearInterval(wsState.activePingTimer);
wsState.activePingTimer = undefined;
}
}
// Expect a pong within a time limit
clearPongTimer(wsKey) {
const wsState = this.wsStore.get(wsKey);
if (wsState === null || wsState === void 0 ? void 0 : wsState.activePongTimer) {
clearTimeout(wsState.activePongTimer);
wsState.activePongTimer = undefined;
}
}
clearReconnectTimer(wsKey) {
const wsState = this.wsStore.get(wsKey);
if (wsState === null || wsState === void 0 ? void 0 : wsState.activeReconnectTimer) {
clearTimeout(wsState.activeReconnectTimer);
wsState.activeReconnectTimer = undefined;
}
}
/**
* @private Use the `subscribe(topics)` method to subscribe to topics. Send WS message to subscribe to topics.
*/
requestSubscribeTopics(wsKey, topics) {
if (!topics.length) {
return;
}
const maxTopicsPerEvent = (0, util_1.getMaxTopicsPerSubscribeEvent)(this.options.market);
if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) {
this.logger.silly(`Subscribing to topics in batches of ${maxTopicsPerEvent}`);
for (var i = 0; i < topics.length; i += maxTopicsPerEvent) {
const batch = topics.slice(i, i + maxTopicsPerEvent);
this.logger.silly(`Subscribing to batch of ${batch.length}`);
this.requestSubscribeTopics(wsKey, batch);
}
this.logger.silly(`Finished batch subscribing to ${topics.length} topics`);
return;
}
const wsMessage = JSON.stringify({
req_id: topics.join(','),
op: 'subscribe',
args: topics,
});
this.tryWsSend(wsKey, wsMessage);
}
/**
* @private Use the `unsubscribe(topics)` method to unsubscribe from topics. Send WS message to unsubscribe from topics.
*/
requestUnsubscribeTopics(wsKey, topics) {
if (!topics.length) {
return;
}
const maxTopicsPerEvent = (0, util_1.getMaxTopicsPerSubscribeEvent)(this.options.market);
if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) {
this.logger.silly(`Unsubscribing to topics in batches of ${maxTopicsPerEvent}`);
for (var i = 0; i < topics.length; i += maxTopicsPerEvent) {
const batch = topics.slice(i, i + maxTopicsPerEvent);
this.logger.silly(`Unsubscribing to batch of ${batch.length}`);
this.requestUnsubscribeTopics(wsKey, batch);
}
this.logger.silly(`Finished batch unsubscribing to ${topics.length} topics`);
return;
}
const wsMessage = JSON.stringify({
op: 'unsubscribe',
args: topics,
});
this.tryWsSend(wsKey, wsMessage);
}
tryWsSend(wsKey, wsMessage) {
try {
this.logger.silly(`Sending upstream ws message: `, {
...loggerCategory,
wsMessage,
wsKey,
});
if (!wsKey) {
throw new Error('Cannot send message due to no known websocket for this wsKey');
}
const ws = this.getWs(wsKey);
if (!ws) {
throw new Error(`${wsKey} socket not connected yet, call "connect(${wsKey}) first then try again when the "open" event arrives`);
}
ws.send(wsMessage);
}
catch (e) {
this.logger.error(`Failed to send WS message`, {
...loggerCategory,
wsMessage,
wsKey,
exception: e,
});
}
}
connectToWsUrl(url, wsKey) {
var _a;
this.logger.silly(`Opening WS connection to URL: ${url}`, {
...loggerCategory,
wsKey,
});
const agent = (_a = this.options.requestOptions) === null || _a === void 0 ? void 0 : _a.agent;
const ws = new isomorphic_ws_1.default(url, undefined, agent ? { agent } : undefined);
ws.onopen = (event) => this.onWsOpen(event, wsKey);
ws.onmessage = (event) => this.onWsMessage(event, wsKey);
ws.onerror = (event) => this.parseWsError('Websocket onWsError', event, wsKey);
ws.onclose = (event) => this.onWsClose(event, wsKey);
return ws;
}
async onWsOpen(event, wsKey) {
if (this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.CONNECTING)) {
this.logger.info('Websocket connected', {
...loggerCategory,
wsKey,
testnet: this.isTestnet(),
market: this.options.market,
});
this.emit('open', { wsKey, event });
}
else if (this.wsStore.isConnectionState(wsKey, util_1.WsConnectionStateEnum.RECONNECTING)) {
this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey });
this.emit('reconnected', { wsKey, event });
}
this.setWsState(wsKey, util_1.WsConnectionStateEnum.CONNECTED);
// Some websockets require an auth packet to be sent after opening the connection
if (util_1.WS_AUTH_ON_CONNECT_KEYS.includes(wsKey)) {
this.logger.info(`Sending auth request...`);
await this.sendAuthRequest(wsKey);
}
// TODO: persistence not working yet for spot v1 topics
if (wsKey !== util_1.WS_KEY_MAP.spotPublic && wsKey !== util_1.WS_KEY_MAP.spotPrivate) {
this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]);
}
this.wsStore.get(wsKey, true).activePingTimer = setInterval(() => this.ping(wsKey), this.options.pingInterval);
}
onWsMessage(event, wsKey) {
try {
// any message can clear the pong timer - wouldn't get a message if the ws dropped
this.clearPongTimer(wsKey);
const msg = JSON.parse((event && event.data) || event);
// this.logger.silly('Received event', {
// ...loggerCategory,
// wsKey,
// msg: JSON.stringify(msg),
// });
// TODO: cleanme
if (msg['success'] || (msg === null || msg === void 0 ? void 0 : msg.pong) || (0, util_1.isWsPong)(msg)) {
if ((0, util_1.isWsPong)(msg)) {
this.logger.silly('Received pong', { ...loggerCategory, wsKey });
}
else {
this.emit('response', { ...msg, wsKey });
}
return;
}
if (msg['finalFragment']) {
return this.emit('response', { ...msg, wsKey });
}
if (msg === null || msg === void 0 ? void 0 : msg.topic) {
return this.emit('update', { ...msg, wsKey });
}
if (
// spot v1
(msg === null || msg === void 0 ? void 0 : msg.code) ||
// spot v3
(msg === null || msg === void 0 ? void 0 : msg.type) === 'error' ||
// usdc options
(msg === null || msg === void 0 ? void 0 : msg.success) === false) {
return this.emit('error', { ...msg, wsKey });
}
this.logger.warning('Unhandled/unrecognised ws event message', {
...loggerCategory,
message: msg,
event,
wsKey,
});
}
catch (e) {
this.logger.error('Failed to parse ws event message', {
...loggerCategory,
error: e,
event,
wsKey,
});
}
}
onWsClose(event, wsKey) {
this.logger.info('Websocket connection closed', {
...loggerCategory,
wsKey,
});
if (this.wsStore.getConnectionState(wsKey) !== util_1.WsConnectionStateEnum.CLOSING) {
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout);
this.emit('reconnect', { wsKey, event });
}
else {
this.setWsState(wsKey, util_1.WsConnectionStateEnum.INITIAL);
this.emit('close', { wsKey, event });
}
}
getWs(wsKey) {
return this.wsStore.getWs(wsKey);
}
setWsState(wsKey, state) {
this.wsStore.setConnectionState(wsKey, state);
}
getWsUrl(wsKey) {
if (this.options.wsUrl) {
return this.options.wsUrl;
}
const networkKey = this.isTestnet() ? 'testnet' : 'livenet';
switch (wsKey) {
case util_1.WS_KEY_MAP.linearPublic: {
return util_1.WS_BASE_URL_MAP.linear.public[networkKey];
}
case util_1.WS_KEY_MAP.linearPrivate: {
return util_1.WS_BASE_URL_MAP.linear.private[networkKey];
}
case util_1.WS_KEY_MAP.spotPublic: {
return util_1.WS_BASE_URL_MAP.spot.public[networkKey];
}
case util_1.WS_KEY_MAP.spotPrivate: {
return util_1.WS_BASE_URL_MAP.spot.private[networkKey];
}
case util_1.WS_KEY_MAP.spotV3Public: {
return util_1.WS_BASE_URL_MAP.spotv3.public[networkKey];
}
case util_1.WS_KEY_MAP.spotV3Private: {
return util_1.WS_BASE_URL_MAP.spotv3.private[networkKey];
}
case util_1.WS_KEY_MAP.inverse: {
// private and public are on the same WS connection
return util_1.WS_BASE_URL_MAP.inverse.public[networkKey];
}
case util_1.WS_KEY_MAP.usdcOptionPublic: {
return util_1.WS_BASE_URL_MAP.usdcOption.public[networkKey];
}
case util_1.WS_KEY_MAP.usdcOptionPrivate: {
return util_1.WS_BASE_URL_MAP.usdcOption.private[networkKey];
}
case util_1.WS_KEY_MAP.usdcPerpPublic: {
return util_1.WS_BASE_URL_MAP.usdcPerp.public[networkKey];
}
case util_1.WS_KEY_MAP.usdcPerpPrivate: {
return util_1.WS_BASE_URL_MAP.usdcPerp.private[networkKey];
}
case util_1.WS_KEY_MAP.unifiedOptionPublic: {
return util_1.WS_BASE_URL_MAP.unifiedOption.public[networkKey];
}
case util_1.WS_KEY_MAP.unifiedPerpUSDTPublic: {
return util_1.WS_BASE_URL_MAP.unifiedPerpUSDT.public[networkKey];
}
case util_1.WS_KEY_MAP.unifiedPerpUSDCPublic: {
return util_1.WS_BASE_URL_MAP.unifiedPerpUSDC.public[networkKey];
}
case util_1.WS_KEY_MAP.unifiedPrivate: {
return util_1.WS_BASE_URL_MAP.unifiedPerp.private[networkKey];
}
case util_1.WS_KEY_MAP.contractInversePrivate: {
return util_1.WS_BASE_URL_MAP.contractInverse.private[networkKey];
}
case util_1.WS_KEY_MAP.contractInversePublic: {
return util_1.WS_BASE_URL_MAP.contractInverse.public[networkKey];
}
case util_1.WS_KEY_MAP.contractUSDTPrivate: {
return util_1.WS_BASE_URL_MAP.contractUSDT.private[networkKey];
}
case util_1.WS_KEY_MAP.contractUSDTPublic: {
return util_1.WS_BASE_URL_MAP.contractUSDT.public[networkKey];
}
// V5 WebSocket keys - these should be handled by WebSocketClientV5
case util_1.WS_KEY_MAP.v5Spot:
case util_1.WS_KEY_MAP.v5Linear:
case util_1.WS_KEY_MAP.v5Inverse:
case util_1.WS_KEY_MAP.v5Option:
case util_1.WS_KEY_MAP.v5Private: {
throw new Error(`V5 WebSocket keys should be handled by WebSocketClientV5, not WebsocketClient. Use WebSocketClientV5 for V5 API connections.`);
}
default: {
this.logger.error('getWsUrl(): Unhandled wsKey: ', {
...loggerCategory,
wsKey,
});
throw (0, util_1.neverGuard)(wsKey, `getWsUrl(): Unhandled wsKey`);
}
}
}
wrongMarketError(market) {
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`);
}
/** @deprecated use "market: 'spotv3" client */
subscribePublicSpotTrades(symbol, binary) {
if (this.options.market !== 'spot') {
throw this.wrongMarketError('spot');
}
return this.tryWsSend(util_1.WS_KEY_MAP.spotPublic, JSON.stringify({
topic: 'trade',
event: 'sub',
symbol,
params: {
binary: !!binary,
},
}));
}
/** @deprecated use "market: 'spotv3" client */
subscribePublicSpotTradingPair(symbol, binary) {
if (this.options.market !== 'spot') {
throw this.wrongMarketError('spot');
}
return this.tryWsSend(util_1.WS_KEY_MAP.spotPublic, JSON.stringify({
symbol,
topic: 'realtimes',
event: 'sub',
params: {
binary: !!binary,
},
}));
}
/** @deprecated use "market: 'spotv3" client */
subscribePublicSpotV1Kline(symbol, candleSize, binary) {
if (this.options.market !== 'spot') {
throw this.wrongMarketError('spot');
}
return this.tryWsSend(util_1.WS_KEY_MAP.spotPublic, JSON.stringify({
symbol,
topic: 'kline_' + candleSize,
event: 'sub',
params: {
binary: !!binary,
},
}));
}
//ws.send('{"symbol":"BTCUSDT","topic":"depth","event":"sub","params":{"binary":false}}');
//ws.send('{"symbol":"BTCUSDT","topic":"mergedDepth","event":"sub","params":{"binary":false,"dumpScale":1}}');
//ws.send('{"symbol":"BTCUSDT","topic":"diffDepth","event":"sub","params":{"binary":false}}');
/** @deprecated use "market: 'spotv3" client */
subscribePublicSpotOrderbook(symbol, depth, dumpScale, binary) {
if (this.options.market !== 'spot') {
throw this.wrongMarketError('spot');
}
let topic;
switch (depth) {
case 'full': {
topic = 'depth';
break;
}
case 'merge': {
topic = 'mergedDepth';
if (!dumpScale) {
throw new Error(`Dumpscale must be provided for merged orderbooks`);
}
break;
}
case 'delta': {
topic = 'diffDepth';
break;
}
}
const msg = {
symbol,
topic,
event: 'sub',
params: {
binary: !!binary,
},
};
if (dumpScale) {
msg.params.dumpScale = dumpScale;
}
return this.tryWsSend(util_1.WS_KEY_MAP.spotPublic, JSON.stringify(msg));
}
}
exports.WebsocketClient = WebsocketClient;
//# sourceMappingURL=websocket-client.js.map