usdc options public ws test
This commit is contained in:
@@ -158,7 +158,7 @@ The WebsocketClient can be configured to a specific API group using the market p
|
||||
| Futures v2 - Inverse Futures | `market: 'inverse'` | The [inverse futures v2](https://bybit-exchange.github.io/docs/futuresV2/inverse_futures/#t-websocket) category uses the same market as inverse perps. |
|
||||
| Spot v3 | `market: 'spotv3'` | The [spot v3](https://bybit-exchange.github.io/docs/spot/v3/#t-websocket) category. |
|
||||
| Spot v1 | `market: 'spot'` | The older [spot v1](https://bybit-exchange.github.io/docs/spot/v1/#t-websocket) category. Use the `spotv3` market if possible, as the v1 category does not have automatic re-subscribe if reconnected. |
|
||||
| Copy Trading | `market: 'linear'` | The [copy trading](https://bybit-exchange.github.io/docs/copy_trading/#t-websocket) category. Use the linear market to listen to private topics. |
|
||||
| Copy Trading | `market: 'linear'` | The [copy trading](https://bybit-exchange.github.io/docs/copy_trading/#t-websocket) category. Use the linear market to listen to all copy trading topics. |
|
||||
| USDC Perps | TBC | The [USDC perps](https://bybit-exchange.github.io/docs/usdc/perpetual/#t-websocket) category. |
|
||||
| USDC Options | TBC | The [USDC options](https://bybit-exchange.github.io/docs/usdc/option/#t-websocket) category. |
|
||||
|
||||
|
||||
@@ -13,9 +13,10 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src';
|
||||
{
|
||||
// key: key,
|
||||
// secret: secret,
|
||||
market: 'linear',
|
||||
// market: 'linear',
|
||||
// market: 'inverse',
|
||||
// market: 'spot',
|
||||
market: 'usdcOption',
|
||||
},
|
||||
logger
|
||||
);
|
||||
@@ -51,10 +52,15 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src';
|
||||
// Linear
|
||||
wsClient.subscribe('trade.BTCUSDT');
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('unsubscribing');
|
||||
wsClient.unsubscribe('trade.BTCUSDT');
|
||||
}, 5 * 1000);
|
||||
// usdc options
|
||||
wsClient.subscribe(`recenttrades.BTC`);
|
||||
wsClient.subscribe(`recenttrades.ETH`);
|
||||
wsClient.subscribe(`recenttrades.SOL`);
|
||||
|
||||
// setTimeout(() => {
|
||||
// console.log('unsubscribing');
|
||||
// wsClient.unsubscribe('trade.BTCUSDT');
|
||||
// }, 5 * 1000);
|
||||
|
||||
// For spot, request public connection first then send required topics on 'open'
|
||||
// wsClient.connectPublic();
|
||||
|
||||
@@ -2,12 +2,14 @@ import { InverseClient } from '../inverse-client';
|
||||
import { LinearClient } from '../linear-client';
|
||||
import { SpotClient } from '../spot-client';
|
||||
import { SpotClientV3 } from '../spot-client-v3';
|
||||
import { USDCOptionClient } from '../usdc-option-client';
|
||||
|
||||
export type RESTClient =
|
||||
| InverseClient
|
||||
| LinearClient
|
||||
| SpotClient
|
||||
| SpotClientV3;
|
||||
| SpotClientV3
|
||||
| USDCOptionClient;
|
||||
|
||||
export type numberInString = string;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RestClientOptions, WS_KEY_MAP } from '../util';
|
||||
|
||||
/** For spot markets, spotV3 is recommended */
|
||||
export type APIMarket = 'inverse' | 'linear' | 'spot' | 'spotv3'; //| 'v3';
|
||||
export type APIMarket = 'inverse' | 'linear' | 'spot' | 'spotv3' | 'usdcOption';
|
||||
|
||||
// Same as inverse futures
|
||||
export type WsPublicInverseTopic =
|
||||
|
||||
@@ -61,15 +61,23 @@ export function getRestBaseUrl(
|
||||
return exchangeBaseUrls.testnet;
|
||||
}
|
||||
|
||||
export function isWsPong(response: any) {
|
||||
if (response.pong || response.ping) {
|
||||
export function isWsPong(msg: any): boolean {
|
||||
if (!msg) {
|
||||
return false;
|
||||
}
|
||||
if (msg.pong || msg.ping) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (msg['op'] === 'pong') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
response.request &&
|
||||
response.request.op === 'ping' &&
|
||||
response.ret_msg === 'pong' &&
|
||||
response.success === true
|
||||
msg.request &&
|
||||
msg.request.op === 'ping' &&
|
||||
msg.ret_msg === 'pong' &&
|
||||
msg.success === true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,18 @@ export const WS_BASE_URL_MAP: Record<
|
||||
testnet: 'wss://stream-testnet.bybit.com/spot/private/v3',
|
||||
},
|
||||
},
|
||||
usdcOption: {
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/trade/option/usdc/public/v1',
|
||||
livenet2: 'wss://stream.bytick.com/trade/option/usdc/public/v1',
|
||||
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/public/v1',
|
||||
},
|
||||
private: {
|
||||
livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1',
|
||||
livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1',
|
||||
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WS_KEY_MAP = {
|
||||
@@ -67,6 +79,10 @@ export const WS_KEY_MAP = {
|
||||
spotPublic: 'spotPublic',
|
||||
spotV3Private: 'spotV3Private',
|
||||
spotV3Public: 'spotV3Public',
|
||||
usdcOptionPrivate: 'usdcOptionPrivate',
|
||||
usdcOptionPublic: 'usdcOptionPublic',
|
||||
// usdcPerpPrivate: 'usdcPerpPrivate',
|
||||
// usdcPerpPublic: 'usdcPerpPublic',
|
||||
} as const;
|
||||
|
||||
export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [WS_KEY_MAP.spotV3Private];
|
||||
@@ -74,51 +90,103 @@ export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [WS_KEY_MAP.spotV3Private];
|
||||
export const PUBLIC_WS_KEYS = [
|
||||
WS_KEY_MAP.linearPublic,
|
||||
WS_KEY_MAP.spotPublic,
|
||||
WS_KEY_MAP.spotV3Public,
|
||||
WS_KEY_MAP.usdcOptionPublic,
|
||||
] as string[];
|
||||
|
||||
export function getLinearWsKeyForTopic(topic: string): WsKey {
|
||||
const privateTopics = [
|
||||
/** 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',
|
||||
];
|
||||
if (privateTopics.includes(topic)) {
|
||||
return WS_KEY_MAP.linearPrivate;
|
||||
}
|
||||
|
||||
return WS_KEY_MAP.linearPublic;
|
||||
}
|
||||
|
||||
export function getSpotWsKeyForTopic(
|
||||
topic: string,
|
||||
apiVersion: 'v1' | 'v3'
|
||||
): WsKey {
|
||||
const privateTopics = [
|
||||
'position',
|
||||
'execution',
|
||||
'order',
|
||||
'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',
|
||||
];
|
||||
|
||||
if (apiVersion === 'v3') {
|
||||
if (privateTopics.includes(topic)) {
|
||||
return WS_KEY_MAP.spotV3Private;
|
||||
export function getWsKeyForTopic(
|
||||
market: APIMarket,
|
||||
topic: string,
|
||||
isPrivate?: boolean
|
||||
): WsKey {
|
||||
const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic);
|
||||
switch (market) {
|
||||
case 'inverse': {
|
||||
return WS_KEY_MAP.inverse;
|
||||
}
|
||||
return WS_KEY_MAP.spotV3Public;
|
||||
case 'linear': {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.linearPrivate
|
||||
: WS_KEY_MAP.linearPublic;
|
||||
}
|
||||
case 'spot': {
|
||||
return isPrivateTopic ? WS_KEY_MAP.spotPrivate : WS_KEY_MAP.spotPublic;
|
||||
}
|
||||
case 'spotv3': {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.spotV3Private
|
||||
: WS_KEY_MAP.spotV3Public;
|
||||
}
|
||||
case 'usdcOption': {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.usdcOptionPrivate
|
||||
: WS_KEY_MAP.usdcOptionPublic;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(market, `getWsKeyForTopic(): Unhandled market`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (privateTopics.includes(topic)) {
|
||||
return WS_KEY_MAP.spotPrivate;
|
||||
export function getUsdcWsKeyForTopic(
|
||||
topic: string,
|
||||
subGroup: 'option' | 'perp'
|
||||
): WsKey {
|
||||
const isPrivateTopic = PRIVATE_TOPICS.includes(topic);
|
||||
if (subGroup === 'option') {
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.usdcOptionPrivate
|
||||
: WS_KEY_MAP.usdcOptionPublic;
|
||||
}
|
||||
return WS_KEY_MAP.spotPublic;
|
||||
return isPrivateTopic
|
||||
? WS_KEY_MAP.usdcOptionPrivate
|
||||
: WS_KEY_MAP.usdcOptionPublic;
|
||||
// return isPrivateTopic
|
||||
// ? WS_KEY_MAP.usdcPerpPrivate
|
||||
// : WS_KEY_MAP.usdcPerpPublic;
|
||||
}
|
||||
|
||||
export const WS_ERROR_ENUM = {
|
||||
NOT_AUTHENTICATED_SPOT_V3: '-1004',
|
||||
BAD_API_KEY_SPOT_V3: '10003',
|
||||
};
|
||||
|
||||
export function neverGuard(x: never, msg: string): Error {
|
||||
return new Error(`Unhandled value exception "x", ${msg}`);
|
||||
}
|
||||
|
||||
@@ -22,19 +22,16 @@ import {
|
||||
import {
|
||||
serializeParams,
|
||||
isWsPong,
|
||||
getLinearWsKeyForTopic,
|
||||
getSpotWsKeyForTopic,
|
||||
WsConnectionStateEnum,
|
||||
PUBLIC_WS_KEYS,
|
||||
WS_AUTH_ON_CONNECT_KEYS,
|
||||
WS_KEY_MAP,
|
||||
DefaultLogger,
|
||||
WS_BASE_URL_MAP,
|
||||
getWsKeyForTopic,
|
||||
neverGuard,
|
||||
} from './util';
|
||||
|
||||
function neverGuard(x: never, msg: string): Error {
|
||||
return new Error(`Unhandled value exception "x", ${msg}`);
|
||||
}
|
||||
import { USDCOptionClient } from './usdc-option-client';
|
||||
|
||||
const loggerCategory = { category: 'bybit-ws' };
|
||||
|
||||
@@ -94,10 +91,8 @@ export class WebsocketClient extends EventEmitter {
|
||||
...options,
|
||||
};
|
||||
|
||||
if (this.options.fetchTimeOffsetBeforeAuth) {
|
||||
this.prepareRESTClient();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used if we fetch exchange time before attempting auth.
|
||||
@@ -148,15 +143,28 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.connectPublic();
|
||||
break;
|
||||
}
|
||||
// if (this.isV3()) {
|
||||
// this.restClient = new SpotClientV3(
|
||||
// undefined,
|
||||
// undefined,
|
||||
// this.isLivenet(),
|
||||
// this.options.restOptions,
|
||||
// this.options.requestOptions
|
||||
// );
|
||||
// }
|
||||
case 'spotv3': {
|
||||
this.restClient = new SpotClientV3(
|
||||
undefined,
|
||||
undefined,
|
||||
!this.isTestnet(),
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
this.connectPublic();
|
||||
break;
|
||||
}
|
||||
case 'usdcOption': {
|
||||
this.restClient = new USDCOptionClient(
|
||||
undefined,
|
||||
undefined,
|
||||
!this.isTestnet(),
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
this.connectPublic();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -208,25 +216,15 @@ export class WebsocketClient extends EventEmitter {
|
||||
public connectAll(): Promise<WebSocket | undefined>[] {
|
||||
switch (this.options.market) {
|
||||
case 'inverse': {
|
||||
return [this.connect(WS_KEY_MAP.inverse)];
|
||||
// only one for inverse
|
||||
return [this.connectPublic()];
|
||||
}
|
||||
case 'linear': {
|
||||
return [
|
||||
this.connect(WS_KEY_MAP.linearPublic),
|
||||
this.connect(WS_KEY_MAP.linearPrivate),
|
||||
];
|
||||
}
|
||||
case 'spot': {
|
||||
return [
|
||||
this.connect(WS_KEY_MAP.spotPublic),
|
||||
this.connect(WS_KEY_MAP.spotPrivate),
|
||||
];
|
||||
}
|
||||
case 'spotv3': {
|
||||
return [
|
||||
this.connect(WS_KEY_MAP.spotV3Public),
|
||||
this.connect(WS_KEY_MAP.spotV3Private),
|
||||
];
|
||||
// these all have separate public & private ws endpoints
|
||||
case 'linear':
|
||||
case 'spot':
|
||||
case 'spotv3':
|
||||
case 'usdcOption': {
|
||||
return [this.connectPublic(), this.connectPrivate()];
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(this.options.market, `connectAll(): Unhandled market`);
|
||||
@@ -248,6 +246,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'spotv3': {
|
||||
return this.connect(WS_KEY_MAP.spotV3Public);
|
||||
}
|
||||
case 'usdcOption': {
|
||||
return this.connect(WS_KEY_MAP.usdcOptionPublic);
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -257,7 +258,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public connectPrivate(): Promise<WebSocket | undefined> | undefined {
|
||||
public connectPrivate(): Promise<WebSocket | undefined> {
|
||||
switch (this.options.market) {
|
||||
case 'inverse': {
|
||||
return this.connect(WS_KEY_MAP.inverse);
|
||||
@@ -271,6 +272,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'spotv3': {
|
||||
return this.connect(WS_KEY_MAP.spotV3Private);
|
||||
}
|
||||
case 'usdcOption': {
|
||||
return this.connect(WS_KEY_MAP.usdcOptionPrivate);
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
@@ -596,10 +600,15 @@ export class WebsocketClient extends EventEmitter {
|
||||
// any message can clear the pong timer - wouldn't get a message if the ws dropped
|
||||
this.clearPongTimer(wsKey);
|
||||
|
||||
// this.logger.silly('Received event', { ...this.logger, wsKey, event });
|
||||
|
||||
const msg = JSON.parse((event && event.data) || event);
|
||||
if (msg['success'] || msg?.pong) {
|
||||
this.logger.silly('Received event', {
|
||||
...this.logger,
|
||||
wsKey,
|
||||
msg: JSON.stringify(msg, null, 2),
|
||||
});
|
||||
|
||||
// TODO: cleanme
|
||||
if (msg['success'] || msg?.pong || isWsPong(msg)) {
|
||||
if (isWsPong(msg)) {
|
||||
this.logger.silly('Received pong', { ...loggerCategory, wsKey });
|
||||
} else {
|
||||
@@ -608,6 +617,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg['finalFragment']) {
|
||||
return this.emit('response', msg);
|
||||
}
|
||||
if (msg?.topic) {
|
||||
return this.emit('update', msg);
|
||||
}
|
||||
@@ -701,6 +713,18 @@ export class WebsocketClient extends EventEmitter {
|
||||
// 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.usdcOption.public[networkKey];
|
||||
// }
|
||||
// case WS_KEY_MAP.usdcPerpPrivate: {
|
||||
// return WS_BASE_URL_MAP.usdcOption.private[networkKey];
|
||||
// }
|
||||
default: {
|
||||
this.logger.error('getWsUrl(): Unhandled wsKey: ', {
|
||||
...loggerCategory,
|
||||
@@ -711,29 +735,6 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private getWsKeyForTopic(topic: string): WsKey {
|
||||
switch (this.options.market) {
|
||||
case 'inverse': {
|
||||
return WS_KEY_MAP.inverse;
|
||||
}
|
||||
case 'linear': {
|
||||
return getLinearWsKeyForTopic(topic);
|
||||
}
|
||||
case 'spot': {
|
||||
return getSpotWsKeyForTopic(topic, 'v1');
|
||||
}
|
||||
case 'spotv3': {
|
||||
return getSpotWsKeyForTopic(topic, 'v3');
|
||||
}
|
||||
default: {
|
||||
throw neverGuard(
|
||||
this.options.market,
|
||||
`connectPublic(): Unhandled market`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
@@ -742,11 +743,16 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Add topic/topics to WS subscription list
|
||||
* @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) {
|
||||
public subscribe(wsTopics: WsTopic[] | WsTopic, isPrivateTopic?: boolean) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
topics.forEach((topic) =>
|
||||
this.wsStore.addTopic(this.getWsKeyForTopic(topic), topic)
|
||||
this.wsStore.addTopic(
|
||||
getWsKeyForTopic(this.options.market, topic, isPrivateTopic),
|
||||
topic
|
||||
)
|
||||
);
|
||||
|
||||
// attempt to send subscription topic per websocket
|
||||
@@ -776,11 +782,16 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Remove topic/topics from WS subscription list
|
||||
* @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) {
|
||||
public unsubscribe(wsTopics: WsTopic[] | WsTopic, isPrivateTopic?: boolean) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
topics.forEach((topic) =>
|
||||
this.wsStore.deleteTopic(this.getWsKeyForTopic(topic), topic)
|
||||
this.wsStore.deleteTopic(
|
||||
getWsKeyForTopic(this.options.market, topic, isPrivateTopic),
|
||||
topic
|
||||
)
|
||||
);
|
||||
|
||||
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
||||
|
||||
79
test/usdc/options/ws.public.test.ts
Normal file
79
test/usdc/options/ws.public.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
WebsocketClient,
|
||||
WSClientConfigurableOptions,
|
||||
WS_KEY_MAP,
|
||||
} from '../../../src';
|
||||
import {
|
||||
logAllEvents,
|
||||
getSilentLogger,
|
||||
waitForSocketEvent,
|
||||
WS_OPEN_EVENT_PARTIAL,
|
||||
} from '../../ws.util';
|
||||
|
||||
describe('Public USDC Option Websocket Client', () => {
|
||||
let wsClient: WebsocketClient;
|
||||
|
||||
const wsClientOptions: WSClientConfigurableOptions = {
|
||||
market: 'usdcOption',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
wsClient = new WebsocketClient(
|
||||
wsClientOptions,
|
||||
getSilentLogger('expectSuccessNoAuth')
|
||||
);
|
||||
// logAllEvents(wsClient);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
wsClient.removeAllListeners();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
wsClient.closeAll();
|
||||
});
|
||||
|
||||
it('should open a public ws connection', async () => {
|
||||
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||
|
||||
expect(wsOpenPromise).resolves.toMatchObject({
|
||||
event: WS_OPEN_EVENT_PARTIAL,
|
||||
wsKey: WS_KEY_MAP.usdcOptionPublic,
|
||||
});
|
||||
|
||||
await Promise.all([wsOpenPromise]);
|
||||
});
|
||||
|
||||
it('should subscribe to public trade events', async () => {
|
||||
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||
|
||||
wsClient.subscribe([
|
||||
'recenttrades.BTC',
|
||||
'recenttrades.ETH',
|
||||
'recenttrades.SOL',
|
||||
]);
|
||||
|
||||
try {
|
||||
expect(await wsResponsePromise).toMatchObject({
|
||||
success: true,
|
||||
data: {
|
||||
failTopics: [],
|
||||
successTopics: expect.any(Array),
|
||||
},
|
||||
type: 'COMMAND_RESP',
|
||||
});
|
||||
} catch (e) {
|
||||
// sub failed
|
||||
expect(e).toBeFalsy();
|
||||
}
|
||||
|
||||
// Takes a while to get an event from USDC options - testing this manually for now
|
||||
// try {
|
||||
// expect(await wsUpdatePromise).toStrictEqual('asdfasdf');
|
||||
// } catch (e) {
|
||||
// // no data
|
||||
// expect(e).toBeFalsy();
|
||||
// }
|
||||
});
|
||||
});
|
||||
@@ -73,6 +73,36 @@ export function waitForSocketEvent(
|
||||
});
|
||||
}
|
||||
|
||||
export function listenToSocketEvents(wsClient: WebsocketClient) {
|
||||
const retVal: Record<
|
||||
'update' | 'open' | 'response' | 'close' | 'error',
|
||||
typeof jest.fn
|
||||
> = {
|
||||
open: jest.fn(),
|
||||
response: jest.fn(),
|
||||
update: jest.fn(),
|
||||
close: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
wsClient.on('open', retVal.open);
|
||||
wsClient.on('response', retVal.response);
|
||||
wsClient.on('update', retVal.update);
|
||||
wsClient.on('close', retVal.close);
|
||||
wsClient.on('error', retVal.error);
|
||||
|
||||
return {
|
||||
...retVal,
|
||||
cleanup: () => {
|
||||
wsClient.removeListener('open', retVal.open);
|
||||
wsClient.removeListener('response', retVal.response);
|
||||
wsClient.removeListener('update', retVal.update);
|
||||
wsClient.removeListener('close', retVal.close);
|
||||
wsClient.removeListener('error', retVal.error);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function logAllEvents(wsClient: WebsocketClient) {
|
||||
wsClient.on('update', (data) => {
|
||||
console.log('wsUpdate: ', JSON.stringify(data, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user