feat(): upgrade WebSocket layer to extend BaseWS abstraction. feat(): add promisified WS workflows, feat(): add WS API integration
This commit is contained in:
136
src/types/websockets/ws-api.ts
Normal file
136
src/types/websockets/ws-api.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { APIID, WS_KEY_MAP } from '../../util';
|
||||
import {
|
||||
AmendOrderParamsV5,
|
||||
CancelOrderParamsV5,
|
||||
OrderParamsV5,
|
||||
} from '../request';
|
||||
import { WsKey } from './ws-general';
|
||||
|
||||
export type WSAPIOperation = 'order.create' | 'order.amend' | 'order.cancel';
|
||||
|
||||
export type WsOperation =
|
||||
| 'subscribe'
|
||||
| 'unsubscribe'
|
||||
| 'auth'
|
||||
| 'ping'
|
||||
| 'pong';
|
||||
|
||||
export const WS_API_Operations: WSAPIOperation[] = [
|
||||
'order.create',
|
||||
'order.amend',
|
||||
'order.cancel',
|
||||
];
|
||||
|
||||
export interface WsRequestOperationBybit<
|
||||
TWSTopic extends string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
||||
// TWSPayload = any,
|
||||
> {
|
||||
req_id: string;
|
||||
op: WsOperation;
|
||||
args?: (TWSTopic | string | number)[];
|
||||
// payload?: TWSPayload;
|
||||
}
|
||||
|
||||
export interface WSAPIRequest<
|
||||
TRequestParams = undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
TWSOperation extends WSAPIOperation = any,
|
||||
> {
|
||||
reqId: string;
|
||||
op: TWSOperation;
|
||||
header: {
|
||||
'X-BAPI-TIMESTAMP': string;
|
||||
'X-BAPI-RECV-WINDOW': string;
|
||||
Referer: typeof APIID;
|
||||
};
|
||||
args: [TRequestParams];
|
||||
}
|
||||
|
||||
export interface WsAPIWsKeyTopicMap {
|
||||
[WS_KEY_MAP.v5PrivateTrade]: WSAPIOperation;
|
||||
}
|
||||
|
||||
export interface WsAPITopicRequestParamMap {
|
||||
'order.create': OrderParamsV5;
|
||||
'order.amend': AmendOrderParamsV5;
|
||||
'order.cancel': CancelOrderParamsV5;
|
||||
// ping: undefined;
|
||||
}
|
||||
|
||||
export type WsAPITopicRequestParams =
|
||||
WsAPITopicRequestParamMap[keyof WsAPITopicRequestParamMap];
|
||||
|
||||
export interface WSAPIResponse<
|
||||
TResponseData extends object = object,
|
||||
TOperation extends WSAPIOperation = WSAPIOperation,
|
||||
> {
|
||||
wsKey: WsKey;
|
||||
/** Auto-generated */
|
||||
reqId: string;
|
||||
retCode: 0 | number;
|
||||
retMsg: 'OK' | string;
|
||||
op: TOperation;
|
||||
data: [TResponseData];
|
||||
header?: {
|
||||
'X-Bapi-Limit': string;
|
||||
'X-Bapi-Limit-Status': string;
|
||||
'X-Bapi-Limit-Reset-Timestamp': string;
|
||||
Traceid: string;
|
||||
Timenow: string;
|
||||
};
|
||||
connId: string;
|
||||
}
|
||||
|
||||
// export interface WsAPIResponseMap<TChannel extends WSAPITopic = WSAPITopic> {
|
||||
// 'spot.login': WSAPIResponse<WSAPILoginResponse, TChannel>;
|
||||
// 'futures.login': WSAPIResponse<WSAPILoginResponse, TChannel>;
|
||||
// string: object;
|
||||
// }
|
||||
|
||||
export interface WsAPIOperationResponseMap<
|
||||
TResponseType extends object = object,
|
||||
> {
|
||||
'order.create': WSAPIResponse<TResponseType, 'order.cancel'>;
|
||||
'order.amend': WSAPIResponse<TResponseType, 'order.amend'>;
|
||||
'order.cancel': WSAPIResponse<TResponseType, 'order.cancel'>;
|
||||
ping: {
|
||||
retCode: 0 | number;
|
||||
retMsg: 'OK' | string;
|
||||
op: 'pong';
|
||||
data: [string];
|
||||
connId: string;
|
||||
};
|
||||
|
||||
// 'spot.login': WSAPIResponse<WSAPILoginResponse, 'spot.login'>;
|
||||
// 'futures.login': WSAPIResponse<WSAPILoginResponse, 'futures.login'>;
|
||||
|
||||
// 'spot.order_place': WSAPIResponse<TResponseType, 'spot.order_place'>;
|
||||
// 'spot.order_cancel': WSAPIResponse<TResponseType, 'spot.order_cancel'>;
|
||||
// 'spot.order_cancel_ids': WSAPIResponse<
|
||||
// TResponseType,
|
||||
// 'spot.order_cancel_ids'
|
||||
// >;
|
||||
// 'spot.order_cancel_cp': WSAPIResponse<TResponseType, 'spot.order_cancel_cp'>;
|
||||
// 'spot.order_amend': WSAPIResponse<TResponseType, 'spot.order_amend'>;
|
||||
// 'spot.order_status': WSAPIResponse<
|
||||
// WSAPIOrderStatusResponse,
|
||||
// 'spot.order_status'
|
||||
// >;
|
||||
// 'futures.order_place': WSAPIResponse<TResponseType[], 'futures.order_place'>;
|
||||
// 'futures.order_batch_place': WSAPIResponse<
|
||||
// TResponseType[],
|
||||
// 'futures.order_batch_place'
|
||||
// >;
|
||||
// 'futures.order_cancel': WSAPIResponse<TResponseType, 'futures.order_cancel'>;
|
||||
// 'futures.order_cancel_cp': WSAPIResponse<
|
||||
// TResponseType,
|
||||
// 'futures.order_cancel_cp'
|
||||
// >;
|
||||
// 'futures.order_amend': WSAPIResponse<TResponseType, 'futures.order_amend'>;
|
||||
// 'futures.order_list': WSAPIResponse<TResponseType[], 'futures.order_list'>;
|
||||
// 'futures.order_status': WSAPIResponse<
|
||||
// WSAPIOrderStatusResponse,
|
||||
// 'futures.order_status'
|
||||
// >;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import WebSocket from 'isomorphic-ws';
|
||||
|
||||
import {
|
||||
CategoryV5,
|
||||
ExecTypeV5,
|
||||
@@ -18,7 +20,23 @@ import {
|
||||
TPSLModeV5,
|
||||
TradeModeV5,
|
||||
} from '../shared-v5';
|
||||
import { WsKey } from '.';
|
||||
|
||||
import { WsKey } from './ws-general';
|
||||
|
||||
export interface MessageEventLike {
|
||||
target: WebSocket;
|
||||
type: 'message';
|
||||
data: string;
|
||||
}
|
||||
|
||||
export function isMessageEvent(msg: unknown): msg is MessageEventLike {
|
||||
if (typeof msg !== 'object' || !msg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const message = msg as MessageEventLike;
|
||||
return message['type'] === 'message' && typeof message['data'] === 'string';
|
||||
}
|
||||
|
||||
export interface WSPublicTopicEventV5<TTopic extends string, TType, TData> {
|
||||
id?: string;
|
||||
|
||||
@@ -82,10 +82,23 @@ export type WsTopic = WsPublicTopics | WsPrivateTopic;
|
||||
|
||||
/** This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets) */
|
||||
export type WsKey = (typeof WS_KEY_MAP)[keyof typeof WS_KEY_MAP];
|
||||
export type WsMarket = 'all';
|
||||
|
||||
export interface WSClientConfigurableOptions {
|
||||
/** Your API key */
|
||||
key?: string;
|
||||
|
||||
/** Your API secret */
|
||||
secret?: string;
|
||||
|
||||
/**
|
||||
* Set to `true` to connect to Bybit's testnet environment.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* - If demo trading, `testnet` should be set to false!
|
||||
* - If testing a strategy, use demo trading instead. Testnet market data is very different from real market conditions.
|
||||
*/
|
||||
testnet?: boolean;
|
||||
|
||||
/**
|
||||
@@ -96,28 +109,54 @@ export interface WSClientConfigurableOptions {
|
||||
demoTrading?: boolean;
|
||||
|
||||
/**
|
||||
* The API group this client should connect to.
|
||||
* The API group this client should connect to. The V5 market is currently used by default.
|
||||
*
|
||||
* For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account asset/copy trading)
|
||||
*/
|
||||
market: APIMarket;
|
||||
market?: APIMarket;
|
||||
|
||||
pongTimeout?: number;
|
||||
pingInterval?: number;
|
||||
reconnectTimeout?: number;
|
||||
/** Override the recv window for authenticating over websockets (default: 5000 ms) */
|
||||
/** Define a recv window when preparing a private websocket signature. This is in milliseconds, so 5000 == 5 seconds */
|
||||
recvWindow?: number;
|
||||
|
||||
/** How often to check if the connection is alive */
|
||||
pingInterval?: number;
|
||||
|
||||
/** How long to wait for a pong (heartbeat reply) before assuming the connection is dead */
|
||||
pongTimeout?: number;
|
||||
|
||||
/** Delay in milliseconds before respawning the connection */
|
||||
reconnectTimeout?: number;
|
||||
|
||||
restOptions?: RestClientOptions;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
requestOptions?: any;
|
||||
wsUrl?: string;
|
||||
/** If true, fetch server time before trying to authenticate (disabled by default) */
|
||||
fetchTimeOffsetBeforeAuth?: boolean;
|
||||
|
||||
/**
|
||||
* Allows you to provide a custom "signMessage" function, e.g. to use node's much faster createHmac method
|
||||
*
|
||||
* Look in the examples folder for a demonstration on using node's createHmac instead.
|
||||
*/
|
||||
customSignMessageFn?: (message: string, secret: string) => Promise<string>;
|
||||
|
||||
/**
|
||||
* If you authenticated the WS API before, automatically try to
|
||||
* re-authenticate the WS API if you're disconnected/reconnected for any reason.
|
||||
*/
|
||||
reauthWSAPIOnReconnect?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* WS configuration that's always defined, regardless of user configuration
|
||||
* (usually comes from defaults if there's no user-provided values)
|
||||
*/
|
||||
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
||||
market: APIMarket;
|
||||
pongTimeout: number;
|
||||
pingInterval: number;
|
||||
reconnectTimeout: number;
|
||||
recvWindow: number;
|
||||
authPrivateConnectionsOnConnect: boolean;
|
||||
authPrivateRequests: boolean;
|
||||
reauthWSAPIOnReconnect: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user