diff --git a/src/index.ts b/src/index.ts index 2a85c6d..1210d85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export * from './rest-client-v5'; export * from './spot-client-v3'; export * from './websocket-client'; +export * from './websocket-api-client'; export * from './util/logger'; export * from './util'; export * from './types'; diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index ed01ed6..9035851 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -124,7 +124,7 @@ export abstract class BaseWebsocketClient< */ private wsStore: WsStore>; - protected logger: typeof DefaultLogger; + public logger: typeof DefaultLogger; protected options: WebsocketClientOptions; diff --git a/src/util/logger.ts b/src/util/logger.ts index 0c5613e..bf6067d 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -3,6 +3,8 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type LogParams = null | any; +export type DefaultLogger = typeof DefaultLogger; + export const DefaultLogger = { /** Ping/pong events and other raw messages that might be noisy. Enable this while troubleshooting. */ trace: (..._params: LogParams): void => { diff --git a/src/websocket-api-client.ts b/src/websocket-api-client.ts new file mode 100644 index 0000000..4e97ab0 --- /dev/null +++ b/src/websocket-api-client.ts @@ -0,0 +1,134 @@ +import { OrderParamsV5, OrderResultV5 } from './types'; +import { WSAPIResponse } from './types/websockets/ws-api'; +import { WSClientConfigurableOptions } from './types/websockets/ws-general'; +import { DefaultLogger } from './util'; +import { WS_KEY_MAP } from './util/websockets/websocket-util'; +import { WebsocketClient } from './websocket-client'; + +/** + * Configurable options specific to only the REST-like WebsocketAPIClient + */ +export interface WSAPIClientConfigurableOptions { + /** + * Default: true + * + * Attach default event listeners, which will console log any high level + * events (opened/reconnecting/reconnected/etc). + * + * If you disable this, you should set your own event listeners + * on the embedded WS Client `wsApiClient.getWSClient().on(....)`. + */ + attachEventListeners: boolean; +} + +/** + * This is a minimal Websocket API wrapper around the WebsocketClient. + * + * Some methods support passing in a custom "wsKey". This is a reference to which WS connection should + * be used to transmit that message. This is only useful if you wish to use an alternative wss + * domain that is supported by the SDK. + * + * Note: To use testnet, don't set the wsKey - use `testnet: true` in + * the constructor instead. + * + * Note: You can also directly use the sendWSAPIRequest() method to make WS API calls, but some + * may find the below methods slightly more intuitive. + * + * Refer to the WS API promises example for a more detailed example on using sendWSAPIRequest() directly: + * https://github.com/tiagosiebler/binance/blob/master/examples/WebSockets/ws-api-raw-promises.ts#L108 + */ +export class WebsocketAPIClient { + private wsClient: WebsocketClient; + + private logger: DefaultLogger; + + private options: WSClientConfigurableOptions & WSAPIClientConfigurableOptions; + + constructor( + options?: WSClientConfigurableOptions & + Partial, + logger?: DefaultLogger, + ) { + this.wsClient = new WebsocketClient(options, logger); + + this.options = { + attachEventListeners: true, + ...options, + }; + + this.logger = this.wsClient.logger; + + this.setupDefaultEventListeners(); + } + + public getWSClient(): WebsocketClient { + return this.wsClient; + } + + public setTimeOffsetMs(newOffset: number): void { + return this.getWSClient().setTimeOffsetMs(newOffset); + } + + /* + * Bybit WebSocket API Methods + * https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline + */ + + /** + * Submit a new order + * + * @param params + * @returns + */ + submitNewOrder( + params: OrderParamsV5, + ): Promise> { + return this.wsClient.sendWSAPIRequest( + WS_KEY_MAP.v5PrivateTrade, + 'order.create', + params, + ); + } + + /** + * + * + * + * + * + * + * + * Private methods for handling some of the convenience/automation provided by the WS API Client + * + * + * + * + * + * + * + */ + + private setupDefaultEventListeners() { + if (this.options.attachEventListeners) { + /** + * General event handlers for monitoring the WebsocketClient + */ + this.wsClient + .on('open', (data) => { + console.log(new Date(), 'ws connected', data.wsKey); + }) + .on('reconnect', ({ wsKey }) => { + console.log(new Date(), 'ws automatically reconnecting.... ', wsKey); + }) + .on('reconnected', (data) => { + console.log(new Date(), 'ws has reconnected ', data?.wsKey); + }) + .on('authenticated', (data) => { + console.info(new Date(), 'ws has authenticated ', data?.wsKey); + }) + .on('exception', (data) => { + console.error(new Date(), 'ws exception: ', JSON.stringify(data)); + }); + } + } +} diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 4f1b485..25eb3ce 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -343,23 +343,23 @@ export class WebsocketClient extends BaseWebsocketClient< // do not trigger excess property checks // Without these overloads, TypeScript won't complain if you include an // unexpected property with your request (if it doesn't clash with an existing property) - sendWSAPIRequest( + sendWSAPIRequest( wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: TWSOpreation, - params: WsAPITopicRequestParamMap[TWSOpreation], - ): Promise; + operation: TWSOperation, + params: WsAPITopicRequestParamMap[TWSOperation], + ): Promise; - sendWSAPIRequest( + sendWSAPIRequest( wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: TWSOpreation, - params: WsAPITopicRequestParamMap[TWSOpreation], - ): Promise; + operation: TWSOperation, + params: WsAPITopicRequestParamMap[TWSOperation], + ): Promise; - sendWSAPIRequest( + sendWSAPIRequest( wsKey: typeof WS_KEY_MAP.v5PrivateTrade, - operation: TWSOpreation, - params: WsAPITopicRequestParamMap[TWSOpreation], - ): Promise; + operation: TWSOperation, + params: WsAPITopicRequestParamMap[TWSOperation], + ): Promise; async sendWSAPIRequest< TWSKey extends keyof WsAPIWsKeyTopicMap,