UNPKG

bybit-api

Version:

Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.

293 lines 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WS_ERROR_ENUM = exports.WS_BASE_URL_MAP = exports.PUBLIC_WS_KEYS = exports.WS_AUTH_ON_CONNECT_KEYS = exports.WS_KEY_MAP = exports.WS_LOGGER_CATEGORY = void 0; exports.isPrivateWsTopic = isPrivateWsTopic; exports.getWsKeyForTopic = getWsKeyForTopic; exports.getWsUrl = getWsUrl; exports.getMaxTopicsPerSubscribeEvent = getMaxTopicsPerSubscribeEvent; exports.safeTerminateWs = safeTerminateWs; exports.getPromiseRefForWSAPIRequest = getPromiseRefForWSAPIRequest; exports.getNormalisedTopicRequests = getNormalisedTopicRequests; exports.getTopicsPerWSKey = getTopicsPerWSKey; const typeGuards_1 = require("../typeGuards"); exports.WS_LOGGER_CATEGORY = { category: 'bybit-ws' }; exports.WS_KEY_MAP = { v5SpotPublic: 'v5SpotPublic', v5LinearPublic: 'v5LinearPublic', v5InversePublic: 'v5InversePublic', v5OptionPublic: 'v5OptionPublic', v5Private: 'v5Private', /** * The V5 Websocket API (for sending orders over WS) */ v5PrivateTrade: 'v5PrivateTrade', }; exports.WS_AUTH_ON_CONNECT_KEYS = [ exports.WS_KEY_MAP.v5Private, exports.WS_KEY_MAP.v5PrivateTrade, ]; exports.PUBLIC_WS_KEYS = [ exports.WS_KEY_MAP.v5SpotPublic, exports.WS_KEY_MAP.v5LinearPublic, exports.WS_KEY_MAP.v5InversePublic, exports.WS_KEY_MAP.v5OptionPublic, ]; /** Used to automatically determine if a sub request should be to the public or private ws (when there's two) */ const PRIVATE_TOPICS = [ 'stop_order', 'outboundAccountInfo', 'executionReport', 'ticketInfo', // copy trading apis 'copyTradePosition', 'copyTradeOrder', 'copyTradeExecution', 'copyTradeWallet', // usdc options 'user.openapi.option.position', 'user.openapi.option.trade', 'user.order', 'user.openapi.option.order', 'user.service', 'user.openapi.greeks', 'user.mmp.event', // usdc perps 'user.openapi.perp.position', 'user.openapi.perp.trade', 'user.openapi.perp.order', 'user.service', // unified margin 'user.position.unifiedAccount', 'user.execution.unifiedAccount', 'user.order.unifiedAccount', 'user.wallet.unifiedAccount', 'user.greeks.unifiedAccount', // contract v3 'user.position.contractAccount', 'user.execution.contractAccount', 'user.order.contractAccount', 'user.wallet.contractAccount', // v5 'position', 'execution', 'order', 'wallet', 'greeks', ]; exports.WS_BASE_URL_MAP = { 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', }, }, v5PrivateTrade: { 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/trade', testnet: 'wss://stream-testnet.bybit.com/v5/trade', }, }, 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', }, }, }; function isPrivateWsTopic(topic) { return PRIVATE_TOPICS.includes(topic); } function getWsKeyForTopic(market, topic, isPrivate, category) { const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic); switch (market) { case 'v5': { if (isPrivateTopic) { return exports.WS_KEY_MAP.v5Private; } switch (category) { case 'spot': { return exports.WS_KEY_MAP.v5SpotPublic; } case 'linear': { return exports.WS_KEY_MAP.v5LinearPublic; } case 'inverse': { return exports.WS_KEY_MAP.v5InversePublic; } case 'option': { return exports.WS_KEY_MAP.v5OptionPublic; } case undefined: { throw new Error('Category cannot be undefined'); } default: { throw (0, typeGuards_1.neverGuard)(category, 'getWsKeyForTopic(v5): Unhandled v5 category'); } } } default: { throw (0, typeGuards_1.neverGuard)(market, 'getWsKeyForTopic(): Unhandled market'); } } } function getWsUrl(wsKey, wsClientOptions, logger) { const wsUrl = wsClientOptions.wsUrl; if (wsUrl) { return wsUrl; } // https://bybit-exchange.github.io/docs/v5/demo const DEMO_TRADING_ENDPOINT = 'wss://stream-demo.bybit.com/v5/private'; const isDemoTrading = wsClientOptions.demoTrading; const isTestnet = wsClientOptions.testnet; const networkKey = isTestnet ? 'testnet' : 'livenet'; switch (wsKey) { case exports.WS_KEY_MAP.v5Private: { if (isDemoTrading) { return DEMO_TRADING_ENDPOINT; } return exports.WS_BASE_URL_MAP.v5.private[networkKey]; } case exports.WS_KEY_MAP.v5PrivateTrade: { if (isDemoTrading) { return DEMO_TRADING_ENDPOINT; } return exports.WS_BASE_URL_MAP[wsKey].private[networkKey]; } case exports.WS_KEY_MAP.v5SpotPublic: { return exports.WS_BASE_URL_MAP.v5SpotPublic.public[networkKey]; } case exports.WS_KEY_MAP.v5LinearPublic: { return exports.WS_BASE_URL_MAP.v5LinearPublic.public[networkKey]; } case exports.WS_KEY_MAP.v5InversePublic: { return exports.WS_BASE_URL_MAP.v5InversePublic.public[networkKey]; } case exports.WS_KEY_MAP.v5OptionPublic: { return exports.WS_BASE_URL_MAP.v5OptionPublic.public[networkKey]; } default: { logger.error('getWsUrl(): Unhandled wsKey: ', { category: 'bybit-ws', wsKey, }); throw (0, typeGuards_1.neverGuard)(wsKey, 'getWsUrl(): Unhandled wsKey'); } } } function getMaxTopicsPerSubscribeEvent(market, wsKey) { switch (market) { case 'v5': { if (wsKey === exports.WS_KEY_MAP.v5SpotPublic) { return 10; } return null; } default: { throw (0, typeGuards_1.neverGuard)(market, 'getWsKeyForTopic(): Unhandled market'); } } } exports.WS_ERROR_ENUM = { NOT_AUTHENTICATED_SPOT_V3: '-1004', API_ERROR_GENERIC: '10001', API_SIGN_AUTH_FAILED: '10003', USDC_OPTION_AUTH_FAILED: '3303006', }; /** * #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. */ function safeTerminateWs( // eslint-disable-next-line @typescript-eslint/no-explicit-any ws, fallbackToClose) { if (!ws) { return false; } if (typeof ws['terminate'] === 'function') { ws.terminate(); return true; } else if (fallbackToClose) { ws.close(); } return false; } /** * WS API promises are stored using a primary key. This key is constructed using * properties found in every request & reply. */ function getPromiseRefForWSAPIRequest(requestEvent) { const promiseRef = [requestEvent.op, requestEvent.reqId].join('_'); return promiseRef; } /** * 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). */ 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; } /** * Groups topics in request into per-wsKey groups * @param normalisedTopicRequests * @param wsKey * @param isPrivateTopic * @returns */ function getTopicsPerWSKey(market, normalisedTopicRequests, wsKey, isPrivateTopic) { const perWsKeyTopics = {}; // Sort into per wsKey arrays, in case topics are mixed together for different wsKeys for (const topicRequest of normalisedTopicRequests) { const derivedWsKey = wsKey || getWsKeyForTopic(market, topicRequest.topic, isPrivateTopic, topicRequest.category); if (!perWsKeyTopics[derivedWsKey] || !Array.isArray(perWsKeyTopics[derivedWsKey])) { perWsKeyTopics[derivedWsKey] = []; } perWsKeyTopics[derivedWsKey].push(topicRequest); } return perWsKeyTopics; } //# sourceMappingURL=websocket-util.js.map