- Returned type improvements.
- Start migrating to base rest client. - Remove deprecated methods. - Run linter. - Deprecate shared endpoints for readibility. All methods are replicated within each client (there's not much duplication). - Expand test coverage for public inverse endpoints.
This commit is contained in:
@@ -1,10 +1,29 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
||||
import {
|
||||
GenericAPIResponse,
|
||||
getRestBaseUrl,
|
||||
RestClientOptions,
|
||||
} from './util/requestUtils';
|
||||
import RequestWrapper from './util/requestWrapper';
|
||||
import SharedEndpoints from './shared-endpoints';
|
||||
import { SymbolFromLimitParam, SymbolIntervalFromLimitParam, SymbolParam } from './types/shared';
|
||||
import {
|
||||
APIResponse,
|
||||
APIResponseWithTime,
|
||||
AssetExchangeRecordsReq,
|
||||
CoinParam,
|
||||
SymbolFromLimitParam,
|
||||
SymbolInfo,
|
||||
SymbolIntervalFromLimitParam,
|
||||
SymbolLimitParam,
|
||||
SymbolParam,
|
||||
SymbolPeriodLimitParam,
|
||||
TimeResult,
|
||||
WalletFundRecordsReq,
|
||||
WithdrawRecordsReq,
|
||||
} from './types/shared';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
|
||||
export class InverseClient extends SharedEndpoints {
|
||||
export class InverseClient extends BaseRestClient {
|
||||
/** @deprecated, */
|
||||
protected requestWrapper: RequestWrapper;
|
||||
|
||||
/**
|
||||
@@ -23,8 +42,13 @@ export class InverseClient extends SharedEndpoints {
|
||||
restClientOptions: RestClientOptions = {},
|
||||
requestOptions: AxiosRequestConfig = {}
|
||||
) {
|
||||
super();
|
||||
|
||||
super(
|
||||
key,
|
||||
secret,
|
||||
getRestBaseUrl(useLivenet, restClientOptions),
|
||||
restClientOptions,
|
||||
requestOptions
|
||||
);
|
||||
this.requestWrapper = new RequestWrapper(
|
||||
key,
|
||||
secret,
|
||||
@@ -35,46 +59,128 @@ export class InverseClient extends SharedEndpoints {
|
||||
return this;
|
||||
}
|
||||
|
||||
async fetchServerTime(): Promise<number> {
|
||||
const res = await this.getServerTime();
|
||||
return Number(res.time_now);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
||||
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/orderBook/L2', params);
|
||||
}
|
||||
|
||||
getKline(
|
||||
params: SymbolIntervalFromLimitParam
|
||||
): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/kline/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use getTickers() instead
|
||||
* Get latest information for symbol
|
||||
*/
|
||||
getLatestInformation(params?: Partial<SymbolParam>): GenericAPIResponse {
|
||||
return this.getTickers(params);
|
||||
getTickers(
|
||||
params?: Partial<SymbolParam>
|
||||
): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/tickers', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use getTrades() instead
|
||||
*/
|
||||
getPublicTradingRecords(params: SymbolFromLimitParam): GenericAPIResponse {
|
||||
return this.getTrades(params);
|
||||
}
|
||||
|
||||
getTrades(params: SymbolFromLimitParam): GenericAPIResponse {
|
||||
getTrades(params: SymbolLimitParam): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/trading-records', params);
|
||||
}
|
||||
|
||||
getMarkPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
||||
getSymbols(): Promise<APIResponseWithTime<SymbolInfo[]>> {
|
||||
return this.requestWrapper.get('v2/public/symbols');
|
||||
}
|
||||
|
||||
getMarkPriceKline(
|
||||
params: SymbolIntervalFromLimitParam
|
||||
): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/mark-price-kline', params);
|
||||
}
|
||||
|
||||
getIndexPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
||||
getIndexPriceKline(
|
||||
params: SymbolIntervalFromLimitParam
|
||||
): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/index-price-kline', params);
|
||||
}
|
||||
|
||||
getPremiumIndexKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
||||
getPremiumIndexKline(
|
||||
params: SymbolIntervalFromLimitParam
|
||||
): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/premium-index-kline', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data : Advanced
|
||||
*
|
||||
*/
|
||||
|
||||
getOpenInterest(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/open-interest', params);
|
||||
}
|
||||
|
||||
getLatestBigDeal(params: SymbolLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/big-deal', params);
|
||||
}
|
||||
|
||||
getLongShortRatio(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/account-ratio', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Account Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getApiKeyInfo(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/api-key');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Wallet Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getWalletBalance(params?: Partial<CoinParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/balance', params);
|
||||
}
|
||||
|
||||
getWalletFundRecords(params?: WalletFundRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/fund/records', params);
|
||||
}
|
||||
|
||||
getWithdrawRecords(params: WithdrawRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/withdraw/list', params);
|
||||
}
|
||||
|
||||
getAssetExchangeRecords(
|
||||
params?: AssetExchangeRecordsReq
|
||||
): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/exchange-order/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* API Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getServerTime(): Promise<APIResponseWithTime<{}>> {
|
||||
return this.requestWrapper.get('v2/public/time');
|
||||
}
|
||||
|
||||
getApiAnnouncements(): Promise<APIResponseWithTime<any[]>> {
|
||||
return this.requestWrapper.get('v2/public/announcement');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Account Data Endpoints
|
||||
@@ -133,8 +239,8 @@ export class InverseClient extends SharedEndpoints {
|
||||
p_r_price?: string;
|
||||
take_profit?: number;
|
||||
stop_loss?: number;
|
||||
tp_trigger_by?:string;
|
||||
sl_trigger_by?:string;
|
||||
tp_trigger_by?: string;
|
||||
sl_trigger_by?: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('v2/private/order/replace', params);
|
||||
}
|
||||
@@ -254,7 +360,10 @@ export class InverseClient extends SharedEndpoints {
|
||||
leverage: number;
|
||||
leverage_only?: boolean;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('v2/private/position/leverage/save', params);
|
||||
return this.requestWrapper.post(
|
||||
'v2/private/position/leverage/save',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,10 +395,7 @@ export class InverseClient extends SharedEndpoints {
|
||||
return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params);
|
||||
}
|
||||
|
||||
setPositionMode(params: {
|
||||
symbol: string;
|
||||
mode: 0 | 3;
|
||||
}): GenericAPIResponse {
|
||||
setPositionMode(params: { symbol: string; mode: 0 | 3 }): GenericAPIResponse {
|
||||
return this.requestWrapper.post('v2/private/position/switch-mode', params);
|
||||
}
|
||||
|
||||
@@ -306,7 +412,10 @@ export class InverseClient extends SharedEndpoints {
|
||||
buy_leverage: number;
|
||||
sell_leverage: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('v2/private/position/switch-isolated', params);
|
||||
return this.requestWrapper.post(
|
||||
'v2/private/position/switch-isolated',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,7 +438,10 @@ export class InverseClient extends SharedEndpoints {
|
||||
*/
|
||||
|
||||
getLastFundingRate(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params);
|
||||
return this.requestWrapper.get(
|
||||
'v2/public/funding/prev-funding-rate',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
getMyLastFundingFee(params: SymbolParam): GenericAPIResponse {
|
||||
@@ -337,7 +449,10 @@ export class InverseClient extends SharedEndpoints {
|
||||
}
|
||||
|
||||
getPredictedFunding(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/funding/predicted-funding', params);
|
||||
return this.requestWrapper.get(
|
||||
'v2/private/funding/predicted-funding',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,5 +467,4 @@ export class InverseClient extends SharedEndpoints {
|
||||
getAPIKeyInfo(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/api-key');
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
||||
import {
|
||||
GenericAPIResponse,
|
||||
getRestBaseUrl,
|
||||
RestClientOptions,
|
||||
} from './util/requestUtils';
|
||||
import RequestWrapper from './util/requestWrapper';
|
||||
import SharedEndpoints from './shared-endpoints';
|
||||
import { SymbolFromLimitParam, SymbolIntervalFromLimitParam, SymbolParam } from './types/shared';
|
||||
import {
|
||||
APIResponse,
|
||||
AssetExchangeRecordsReq,
|
||||
CoinParam,
|
||||
SymbolFromLimitParam,
|
||||
SymbolInfo,
|
||||
SymbolIntervalFromLimitParam,
|
||||
SymbolLimitParam,
|
||||
SymbolParam,
|
||||
SymbolPeriodLimitParam,
|
||||
TimeResult,
|
||||
WalletFundRecordsReq,
|
||||
WithdrawRecordsReq,
|
||||
} from './types/shared';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
|
||||
export class InverseFuturesClient extends SharedEndpoints {
|
||||
export class InverseFuturesClient extends BaseRestClient {
|
||||
/** @deprecated, */
|
||||
protected requestWrapper: RequestWrapper;
|
||||
|
||||
/**
|
||||
@@ -23,8 +41,13 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
restClientOptions: RestClientOptions = {},
|
||||
requestOptions: AxiosRequestConfig = {}
|
||||
) {
|
||||
super();
|
||||
|
||||
super(
|
||||
key,
|
||||
secret,
|
||||
getRestBaseUrl(useLivenet, restClientOptions),
|
||||
restClientOptions,
|
||||
requestOptions
|
||||
);
|
||||
this.requestWrapper = new RequestWrapper(
|
||||
key,
|
||||
secret,
|
||||
@@ -35,6 +58,98 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
return this;
|
||||
}
|
||||
|
||||
async fetchServerTime(): Promise<number> {
|
||||
const res = await this.getServerTime();
|
||||
return res.time_now;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getOrderBook(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/orderBook/L2', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest information for symbol
|
||||
*/
|
||||
getTickers(params?: Partial<SymbolParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/tickers', params);
|
||||
}
|
||||
|
||||
getSymbols(): Promise<APIResponse<SymbolInfo[]>> {
|
||||
return this.requestWrapper.get('v2/public/symbols');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data : Advanced
|
||||
*
|
||||
*/
|
||||
|
||||
getOpenInterest(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/open-interest', params);
|
||||
}
|
||||
|
||||
getLatestBigDeal(params: SymbolLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/big-deal', params);
|
||||
}
|
||||
|
||||
getLongShortRatio(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/account-ratio', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Account Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getApiKeyInfo(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/api-key');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Wallet Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getWalletBalance(params?: Partial<CoinParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/balance', params);
|
||||
}
|
||||
|
||||
getWalletFundRecords(params?: WalletFundRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/fund/records', params);
|
||||
}
|
||||
|
||||
getWithdrawRecords(params: WithdrawRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/withdraw/list', params);
|
||||
}
|
||||
|
||||
getAssetExchangeRecords(
|
||||
params?: AssetExchangeRecordsReq
|
||||
): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/exchange-order/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* API Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getServerTime(): GenericAPIResponse<TimeResult> {
|
||||
return this.requestWrapper.get('v2/public/time');
|
||||
}
|
||||
|
||||
getApiAnnouncements(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/announcement');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
@@ -60,7 +175,9 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
return this.requestWrapper.get('v2/public/index-price-kline', params);
|
||||
}
|
||||
|
||||
getPremiumIndexKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
||||
getPremiumIndexKline(
|
||||
params: SymbolIntervalFromLimitParam
|
||||
): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/premium-index-kline', params);
|
||||
}
|
||||
|
||||
@@ -70,7 +187,7 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* Active orders
|
||||
*/
|
||||
|
||||
@@ -87,7 +204,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
close_on_trigger?: boolean;
|
||||
order_link_id?: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/order/create', orderRequest);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/order/create',
|
||||
orderRequest
|
||||
);
|
||||
}
|
||||
|
||||
getActiveOrderList(params: {
|
||||
@@ -130,7 +250,7 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
return this.requestWrapper.get('futures/private/order', params);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Conditional orders
|
||||
*/
|
||||
|
||||
@@ -147,7 +267,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
close_on_trigger?: boolean;
|
||||
order_link_id?: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/stop-order/create', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/stop-order/create',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
getConditionalOrder(params: {
|
||||
@@ -165,11 +288,17 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
stop_order_id?: string;
|
||||
order_link_id?: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/stop-order/cancel', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/stop-order/cancel',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
cancelAllConditionalOrders(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/stop-order/cancelAll', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/stop-order/cancelAll',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
replaceConditionalOrder(params: {
|
||||
@@ -180,7 +309,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
p_r_price?: string;
|
||||
p_r_trigger_price?: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/stop-order/replace', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/stop-order/replace',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
queryConditionalOrder(params: {
|
||||
@@ -191,11 +323,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
return this.requestWrapper.get('futures/private/stop-order', params);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Position
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Get position list
|
||||
*/
|
||||
@@ -207,7 +338,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
symbol: string;
|
||||
margin: string;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/position/change-position-margin', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/position/change-position-margin',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
setTradingStop(params: {
|
||||
@@ -219,7 +353,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
sl_trigger_by?: string;
|
||||
new_trailing_active?: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/position/trading-stop', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/position/trading-stop',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
setUserLeverage(params: {
|
||||
@@ -227,7 +364,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
buy_leverage: number;
|
||||
sell_leverage: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/position/leverage/save', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/position/leverage/save',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +377,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
symbol: string;
|
||||
mode: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/position/switch-mode', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/position/switch-mode',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +392,10 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
buy_leverage: number;
|
||||
sell_leverage: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.post('futures/private/position/switch-isolated', params);
|
||||
return this.requestWrapper.post(
|
||||
'futures/private/position/switch-isolated',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
getTradeRecords(params: {
|
||||
@@ -271,14 +417,17 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): GenericAPIResponse {
|
||||
return this.requestWrapper.get('futures/private/trade/closed-pnl/list', params);
|
||||
return this.requestWrapper.get(
|
||||
'futures/private/trade/closed-pnl/list',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
**** The following are all the same as the inverse client ****
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* Risk Limit
|
||||
*/
|
||||
getRiskLimitList(): GenericAPIResponse {
|
||||
@@ -292,12 +441,15 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
return this.requestWrapper.post('open-api/wallet/risk-limit', params);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Funding
|
||||
*/
|
||||
|
||||
getLastFundingRate(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params);
|
||||
return this.requestWrapper.get(
|
||||
'v2/public/funding/prev-funding-rate',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
getMyLastFundingFee(params: SymbolParam): GenericAPIResponse {
|
||||
@@ -305,14 +457,17 @@ export class InverseFuturesClient extends SharedEndpoints {
|
||||
}
|
||||
|
||||
getPredictedFunding(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/funding/predicted-funding', params);
|
||||
return this.requestWrapper.get(
|
||||
'v2/private/funding/predicted-funding',
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* LCP Info
|
||||
*/
|
||||
|
||||
getLcpInfo(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/lcp', params);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,19 +5,28 @@ import {
|
||||
RestClientOptions,
|
||||
} from './util/requestUtils';
|
||||
import RequestWrapper from './util/requestWrapper';
|
||||
import SharedEndpoints from './shared-endpoints';
|
||||
import {
|
||||
APIResponse,
|
||||
AssetExchangeRecordsReq,
|
||||
CoinParam,
|
||||
SymbolInfo,
|
||||
SymbolIntervalFromLimitParam,
|
||||
SymbolLimitParam,
|
||||
SymbolParam,
|
||||
SymbolPeriodLimitParam,
|
||||
TimeResult,
|
||||
WalletFundRecordsReq,
|
||||
WithdrawRecordsReq,
|
||||
} from './types/shared';
|
||||
import { linearPositionModeEnum, positionTpSlModeEnum } from './constants/enum';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
|
||||
export class LinearClient extends SharedEndpoints {
|
||||
export class LinearClient extends BaseRestClient {
|
||||
/** @deprecated, */
|
||||
protected requestWrapper: RequestWrapper;
|
||||
|
||||
/**
|
||||
* @public Creates an instance of the linear REST API client.
|
||||
* @public Creates an instance of the linear (USD Perps) REST API client.
|
||||
*
|
||||
* @param {string} key - your API key
|
||||
* @param {string} secret - your API secret
|
||||
@@ -32,7 +41,13 @@ export class LinearClient extends SharedEndpoints {
|
||||
restClientOptions: RestClientOptions = {},
|
||||
requestOptions: AxiosRequestConfig = {}
|
||||
) {
|
||||
super();
|
||||
super(
|
||||
key,
|
||||
secret,
|
||||
getRestBaseUrl(useLivenet, restClientOptions),
|
||||
restClientOptions,
|
||||
requestOptions
|
||||
);
|
||||
|
||||
this.requestWrapper = new RequestWrapper(
|
||||
key,
|
||||
@@ -44,6 +59,98 @@ export class LinearClient extends SharedEndpoints {
|
||||
return this;
|
||||
}
|
||||
|
||||
async fetchServerTime(): Promise<number> {
|
||||
const timeRes = await this.getServerTime();
|
||||
return timeRes.time_now;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getOrderBook(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/orderBook/L2', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest information for symbol
|
||||
*/
|
||||
getTickers(params?: Partial<SymbolParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/tickers', params);
|
||||
}
|
||||
|
||||
getSymbols(): Promise<APIResponse<SymbolInfo[]>> {
|
||||
return this.requestWrapper.get('v2/public/symbols');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data : Advanced
|
||||
*
|
||||
*/
|
||||
|
||||
getOpenInterest(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/open-interest', params);
|
||||
}
|
||||
|
||||
getLatestBigDeal(params: SymbolLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/big-deal', params);
|
||||
}
|
||||
|
||||
getLongShortRatio(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/account-ratio', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Account Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getApiKeyInfo(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/api-key');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Wallet Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getWalletBalance(params?: Partial<CoinParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/balance', params);
|
||||
}
|
||||
|
||||
getWalletFundRecords(params?: WalletFundRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/fund/records', params);
|
||||
}
|
||||
|
||||
getWithdrawRecords(params: WithdrawRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/withdraw/list', params);
|
||||
}
|
||||
|
||||
getAssetExchangeRecords(
|
||||
params?: AssetExchangeRecordsReq
|
||||
): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/exchange-order/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* API Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getServerTime(): GenericAPIResponse<TimeResult> {
|
||||
return this.requestWrapper.get('v2/public/time');
|
||||
}
|
||||
|
||||
getApiAnnouncements(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/announcement');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import {
|
||||
APIResponse,
|
||||
AssetExchangeRecordsReq,
|
||||
CoinParam,
|
||||
SymbolInfo,
|
||||
SymbolLimitParam,
|
||||
SymbolParam,
|
||||
SymbolPeriodLimitParam,
|
||||
WalletFundRecordsReq,
|
||||
WithdrawRecordsReq,
|
||||
} from './types/shared';
|
||||
import { GenericAPIResponse } from './util/requestUtils';
|
||||
import RequestWrapper from './util/requestWrapper';
|
||||
|
||||
export default class SharedEndpoints {
|
||||
// TODO: Is there a way to say that Base has to provide this?
|
||||
protected requestWrapper: RequestWrapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getOrderBook(params: SymbolParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/orderBook/L2', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest information for symbol
|
||||
*/
|
||||
getTickers(params?: Partial<SymbolParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/tickers', params);
|
||||
}
|
||||
|
||||
getSymbols(): Promise<APIResponse<SymbolInfo[]>> {
|
||||
return this.requestWrapper.get('v2/public/symbols');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Market Data : Advanced
|
||||
*
|
||||
*/
|
||||
|
||||
getOpenInterest(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/open-interest', params);
|
||||
}
|
||||
|
||||
getLatestBigDeal(params: SymbolLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/big-deal', params);
|
||||
}
|
||||
|
||||
getLongShortRatio(params: SymbolPeriodLimitParam): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/account-ratio', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Account Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getApiKeyInfo(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/account/api-key');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Wallet Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getWalletBalance(params?: Partial<CoinParam>): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/balance', params)
|
||||
}
|
||||
|
||||
getWalletFundRecords(params?: WalletFundRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/fund/records', params);
|
||||
}
|
||||
|
||||
getWithdrawRecords(params: WithdrawRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/wallet/withdraw/list', params);
|
||||
}
|
||||
|
||||
getAssetExchangeRecords(params?: AssetExchangeRecordsReq): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/private/exchange-order/list', params);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* API Data Endpoints
|
||||
*
|
||||
*/
|
||||
|
||||
getServerTime(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/time');
|
||||
}
|
||||
|
||||
getApiAnnouncements(): GenericAPIResponse {
|
||||
return this.requestWrapper.get('v2/public/announcement');
|
||||
}
|
||||
|
||||
async getTimeOffset(): Promise<number> {
|
||||
const start = Date.now();
|
||||
return this.getServerTime().then(result => {
|
||||
const end = Date.now();
|
||||
return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { KlineInterval } from './types/shared';
|
||||
import { NewSpotOrder, OrderSide, OrderTypeSpot, SpotOrderQueryById } from './types/spot';
|
||||
import {
|
||||
NewSpotOrder,
|
||||
OrderSide,
|
||||
OrderTypeSpot,
|
||||
SpotOrderQueryById,
|
||||
} from './types/spot';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
||||
import { getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
||||
import RequestWrapper from './util/requestWrapper';
|
||||
|
||||
export class SpotClient extends BaseRestClient {
|
||||
@@ -24,19 +29,22 @@ export class SpotClient extends BaseRestClient {
|
||||
restClientOptions: RestClientOptions = {},
|
||||
requestOptions: AxiosRequestConfig = {}
|
||||
) {
|
||||
super(key, secret, getRestBaseUrl(useLivenet, restClientOptions), restClientOptions, requestOptions);
|
||||
super(
|
||||
key,
|
||||
secret,
|
||||
getRestBaseUrl(useLivenet, restClientOptions),
|
||||
restClientOptions,
|
||||
requestOptions
|
||||
);
|
||||
|
||||
// this.requestWrapper = new RequestWrapper(
|
||||
// key,
|
||||
// secret,
|
||||
// getRestBaseUrl(useLivenet, restClientOptions),
|
||||
// restClientOptions,
|
||||
// requestOptions
|
||||
// );
|
||||
return this;
|
||||
}
|
||||
|
||||
async getServerTime(urlKeyOverride?: string): Promise<number> {
|
||||
fetchServerTime(): Promise<number> {
|
||||
return this.getServerTime();
|
||||
}
|
||||
|
||||
async getServerTime(): Promise<number> {
|
||||
const result = await this.get('/spot/v1/time');
|
||||
return result.serverTime;
|
||||
}
|
||||
@@ -45,7 +53,7 @@ export class SpotClient extends BaseRestClient {
|
||||
*
|
||||
* Market Data Endpoints
|
||||
*
|
||||
**/
|
||||
**/
|
||||
|
||||
getSymbols() {
|
||||
return this.get('/spot/v1/symbols');
|
||||
@@ -53,7 +61,8 @@ export class SpotClient extends BaseRestClient {
|
||||
|
||||
getOrderBook(symbol: string, limit?: number) {
|
||||
return this.get('/spot/quote/v1/depth', {
|
||||
symbol, limit
|
||||
symbol,
|
||||
limit,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,7 +81,13 @@ export class SpotClient extends BaseRestClient {
|
||||
});
|
||||
}
|
||||
|
||||
getCandles(symbol: string, interval: KlineInterval, limit?: number, startTime?: number, endTime?: number) {
|
||||
getCandles(
|
||||
symbol: string,
|
||||
interval: KlineInterval,
|
||||
limit?: number,
|
||||
startTime?: number,
|
||||
endTime?: number
|
||||
) {
|
||||
return this.get('/spot/quote/v1/kline', {
|
||||
symbol,
|
||||
interval,
|
||||
@@ -113,9 +128,11 @@ export class SpotClient extends BaseRestClient {
|
||||
cancelOrderBatch(params: {
|
||||
symbol: string;
|
||||
side?: OrderSide;
|
||||
orderTypes: OrderTypeSpot[]
|
||||
orderTypes: OrderTypeSpot[];
|
||||
}) {
|
||||
const orderTypes = params.orderTypes ? params.orderTypes.join(',') : undefined;
|
||||
const orderTypes = params.orderTypes
|
||||
? params.orderTypes.join(',')
|
||||
: undefined;
|
||||
return this.deletePrivate('/spot/order/batch-cancel', {
|
||||
...params,
|
||||
orderTypes,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export type KlineInterval = '1m'
|
||||
export type KlineInterval =
|
||||
| '1m'
|
||||
| '3m'
|
||||
| '5m'
|
||||
| '15m'
|
||||
@@ -16,12 +17,17 @@ export type numberInString = string;
|
||||
|
||||
export interface APIResponse<T> {
|
||||
ret_code: number;
|
||||
ret_msg: "OK" | string;
|
||||
ret_msg: 'OK' | string;
|
||||
ext_code: string;
|
||||
ext_info: string;
|
||||
result: T;
|
||||
}
|
||||
|
||||
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
||||
/** UTC timestamp */
|
||||
time_now: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request Parameter Types
|
||||
*/
|
||||
@@ -116,3 +122,7 @@ export interface SymbolInfo {
|
||||
price_filter: PriceFilter;
|
||||
lot_size_filter: LotSizeFilter;
|
||||
}
|
||||
|
||||
export interface TimeResult {
|
||||
time_now: number;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Method } from 'axios';
|
||||
import axios, {
|
||||
AxiosError,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
Method,
|
||||
} from 'axios';
|
||||
|
||||
import { signMessage } from './node-support';
|
||||
import { RestClientOptions, GenericAPIResponse, getRestBaseUrl, serializeParams, isPublicEndpoint } from './requestUtils';
|
||||
import {
|
||||
RestClientOptions,
|
||||
GenericAPIResponse,
|
||||
getRestBaseUrl,
|
||||
serializeParams,
|
||||
isPublicEndpoint,
|
||||
} from './requestUtils';
|
||||
|
||||
export default abstract class BaseRestClient {
|
||||
private timeOffset: number | null;
|
||||
@@ -12,6 +23,9 @@ export default abstract class BaseRestClient {
|
||||
private key: string | undefined;
|
||||
private secret: string | undefined;
|
||||
|
||||
/** Function that calls exchange API to query & resolve server time, used by time sync */
|
||||
abstract fetchServerTime(): Promise<number>;
|
||||
|
||||
constructor(
|
||||
key: string | undefined,
|
||||
secret: string | undefined,
|
||||
@@ -28,7 +42,7 @@ export default abstract class BaseRestClient {
|
||||
sync_interval_ms: 3600000,
|
||||
// if true, we'll throw errors if any params are undefined
|
||||
strict_param_validation: false,
|
||||
...options
|
||||
...options,
|
||||
};
|
||||
|
||||
this.globalRequestOptions = {
|
||||
@@ -37,14 +51,16 @@ export default abstract class BaseRestClient {
|
||||
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
||||
...requestOptions,
|
||||
headers: {
|
||||
'x-referer': 'bybitapinode'
|
||||
'x-referer': 'bybitapinode',
|
||||
},
|
||||
};
|
||||
|
||||
this.baseUrl = baseUrl;
|
||||
|
||||
if (key && !secret) {
|
||||
throw new Error('API Key & Secret are both required for private enpoints')
|
||||
throw new Error(
|
||||
'API Key & Secret are both required for private enpoints'
|
||||
);
|
||||
}
|
||||
|
||||
if (this.options.disable_time_sync !== true) {
|
||||
@@ -79,7 +95,12 @@ export default abstract class BaseRestClient {
|
||||
/**
|
||||
* @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed.
|
||||
*/
|
||||
private async _call(method: Method, endpoint: string, params?: any, isPublicApi?: boolean): GenericAPIResponse {
|
||||
private async _call(
|
||||
method: Method,
|
||||
endpoint: string,
|
||||
params?: any,
|
||||
isPublicApi?: boolean
|
||||
): GenericAPIResponse {
|
||||
if (!isPublicApi) {
|
||||
if (!this.key || !this.secret) {
|
||||
throw new Error('Private endpoints require api and private keys set');
|
||||
@@ -96,7 +117,7 @@ export default abstract class BaseRestClient {
|
||||
...this.globalRequestOptions,
|
||||
url: [this.baseUrl, endpoint].join(endpoint.startsWith('/') ? '' : '/'),
|
||||
method: method,
|
||||
json: true
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (method === 'GET') {
|
||||
@@ -105,13 +126,15 @@ export default abstract class BaseRestClient {
|
||||
options.data = params;
|
||||
}
|
||||
|
||||
return axios(options).then(response => {
|
||||
if (response.status == 200) {
|
||||
return response.data;
|
||||
}
|
||||
return axios(options)
|
||||
.then((response) => {
|
||||
if (response.status == 200) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
throw response;
|
||||
}).catch(e => this.parseException(e));
|
||||
throw response;
|
||||
})
|
||||
.catch((e) => this.parseException(e));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,7 +163,7 @@ export default abstract class BaseRestClient {
|
||||
message: response.statusText,
|
||||
body: response.data,
|
||||
headers: response.headers,
|
||||
requestOptions: this.options
|
||||
requestOptions: this.options,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,7 +174,7 @@ export default abstract class BaseRestClient {
|
||||
const params = {
|
||||
...data,
|
||||
api_key: this.key,
|
||||
timestamp: Date.now() + (this.timeOffset || 0)
|
||||
timestamp: Date.now() + (this.timeOffset || 0),
|
||||
};
|
||||
|
||||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
||||
@@ -160,7 +183,10 @@ export default abstract class BaseRestClient {
|
||||
}
|
||||
|
||||
if (this.key && this.secret) {
|
||||
const serializedParams = serializeParams(params, this.options.strict_param_validation);
|
||||
const serializedParams = serializeParams(
|
||||
params,
|
||||
this.options.strict_param_validation
|
||||
);
|
||||
params.sign = await signMessage(serializedParams, this.secret);
|
||||
}
|
||||
|
||||
@@ -179,7 +205,7 @@ export default abstract class BaseRestClient {
|
||||
return this.syncTimePromise;
|
||||
}
|
||||
|
||||
this.syncTimePromise = this.fetchTimeOffset().then(offset => {
|
||||
this.syncTimePromise = this.fetchTimeOffset().then((offset) => {
|
||||
this.timeOffset = offset;
|
||||
this.syncTimePromise = null;
|
||||
});
|
||||
@@ -187,22 +213,27 @@ export default abstract class BaseRestClient {
|
||||
return this.syncTimePromise;
|
||||
}
|
||||
|
||||
abstract getServerTime(baseUrlKeyOverride?: string): Promise<number>;
|
||||
|
||||
/**
|
||||
* Estimate drift based on client<->server latency
|
||||
*/
|
||||
async fetchTimeOffset(): Promise<number> {
|
||||
try {
|
||||
const start = Date.now();
|
||||
const serverTime = await this.getServerTime();
|
||||
const serverTime = await this.fetchServerTime();
|
||||
|
||||
if (!serverTime || isNaN(serverTime)) {
|
||||
throw new Error(
|
||||
`fetchServerTime() returned non-number: "${serverTime}" typeof(${typeof serverTime})`
|
||||
);
|
||||
}
|
||||
|
||||
const end = Date.now();
|
||||
|
||||
const avgDrift = ((end - start) / 2);
|
||||
const avgDrift = (end - start) / 2;
|
||||
return Math.ceil(serverTime - end + avgDrift);
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch get time offset: ', e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,25 +19,33 @@ export interface RestClientOptions {
|
||||
parse_exceptions?: boolean;
|
||||
}
|
||||
|
||||
export type GenericAPIResponse = Promise<any>;
|
||||
export type GenericAPIResponse<T = any> = Promise<T>;
|
||||
|
||||
export function serializeParams(params: object = {}, strict_validation = false): string {
|
||||
export function serializeParams(
|
||||
params: object = {},
|
||||
strict_validation = false
|
||||
): string {
|
||||
return Object.keys(params)
|
||||
.sort()
|
||||
.map(key => {
|
||||
.map((key) => {
|
||||
const value = params[key];
|
||||
if (strict_validation === true && typeof value === 'undefined') {
|
||||
throw new Error('Failed to sign API request due to undefined parameter');
|
||||
throw new Error(
|
||||
'Failed to sign API request due to undefined parameter'
|
||||
);
|
||||
}
|
||||
return `${key}=${value}`;
|
||||
})
|
||||
.join('&');
|
||||
};
|
||||
}
|
||||
|
||||
export function getRestBaseUrl(useLivenet: boolean, restInverseOptions: RestClientOptions) {
|
||||
export function getRestBaseUrl(
|
||||
useLivenet: boolean,
|
||||
restInverseOptions: RestClientOptions
|
||||
) {
|
||||
const baseUrlsInverse = {
|
||||
livenet: 'https://api.bybit.com',
|
||||
testnet: 'https://api-testnet.bybit.com'
|
||||
testnet: 'https://api-testnet.bybit.com',
|
||||
};
|
||||
|
||||
if (restInverseOptions.baseUrl) {
|
||||
@@ -50,7 +58,7 @@ export function getRestBaseUrl(useLivenet: boolean, restInverseOptions: RestClie
|
||||
return baseUrlsInverse.testnet;
|
||||
}
|
||||
|
||||
export function isPublicEndpoint (endpoint: string): boolean {
|
||||
export function isPublicEndpoint(endpoint: string): boolean {
|
||||
if (endpoint.startsWith('v2/public')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,20 +12,20 @@ import WsStore from './util/WsStore';
|
||||
|
||||
const inverseEndpoints = {
|
||||
livenet: 'wss://stream.bybit.com/realtime',
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime'
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime',
|
||||
};
|
||||
|
||||
const linearEndpoints = {
|
||||
private: {
|
||||
livenet: 'wss://stream.bybit.com/realtime_private',
|
||||
livenet2: 'wss://stream.bytick.com/realtime_private',
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime_private'
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
|
||||
},
|
||||
public: {
|
||||
livenet: 'wss://stream.bybit.com/realtime_public',
|
||||
livenet2: 'wss://stream.bytick.com/realtime_public',
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime_public'
|
||||
}
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
|
||||
},
|
||||
};
|
||||
|
||||
const spotEndpoints = {
|
||||
@@ -38,8 +38,8 @@ const spotEndpoints = {
|
||||
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
|
||||
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
|
||||
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const loggerCategory = { category: 'bybit-ws' };
|
||||
|
||||
@@ -54,62 +54,71 @@ export enum WsConnectionState {
|
||||
READY_STATE_CONNECTING,
|
||||
READY_STATE_CONNECTED,
|
||||
READY_STATE_CLOSING,
|
||||
READY_STATE_RECONNECTING
|
||||
};
|
||||
READY_STATE_RECONNECTING,
|
||||
}
|
||||
|
||||
export type APIMarket = 'inverse' | 'linear' | 'spot';
|
||||
|
||||
// Same as inverse futures
|
||||
export type WsPublicInverseTopic = 'orderBookL2_25'
|
||||
export type WsPublicInverseTopic =
|
||||
| 'orderBookL2_25'
|
||||
| 'orderBookL2_200'
|
||||
| 'trade'
|
||||
| 'insurance'
|
||||
| 'instrument_info'
|
||||
| 'klineV2';
|
||||
|
||||
export type WsPublicUSDTPerpTopic = 'orderBookL2_25'
|
||||
export type WsPublicUSDTPerpTopic =
|
||||
| 'orderBookL2_25'
|
||||
| 'orderBookL2_200'
|
||||
| 'trade'
|
||||
| 'insurance'
|
||||
| 'instrument_info'
|
||||
| 'kline';
|
||||
|
||||
export type WsPublicSpotV1Topic = 'trade'
|
||||
export type WsPublicSpotV1Topic =
|
||||
| 'trade'
|
||||
| 'realtimes'
|
||||
| 'kline'
|
||||
| 'depth'
|
||||
| 'mergedDepth'
|
||||
| 'diffDepth';
|
||||
|
||||
export type WsPublicSpotV2Topic = 'depth'
|
||||
export type WsPublicSpotV2Topic =
|
||||
| 'depth'
|
||||
| 'kline'
|
||||
| 'trade'
|
||||
| 'bookTicker'
|
||||
| 'realtimes';
|
||||
|
||||
export type WsPublicTopics = WsPublicInverseTopic
|
||||
export type WsPublicTopics =
|
||||
| WsPublicInverseTopic
|
||||
| WsPublicUSDTPerpTopic
|
||||
| WsPublicSpotV1Topic
|
||||
| WsPublicSpotV2Topic
|
||||
| string;
|
||||
|
||||
// Same as inverse futures
|
||||
export type WsPrivateInverseTopic = 'position'
|
||||
export type WsPrivateInverseTopic =
|
||||
| 'position'
|
||||
| 'execution'
|
||||
| 'order'
|
||||
| 'stop_order';
|
||||
|
||||
export type WsPrivateUSDTPerpTopic = 'position'
|
||||
export type WsPrivateUSDTPerpTopic =
|
||||
| 'position'
|
||||
| 'execution'
|
||||
| 'order'
|
||||
| 'stop_order'
|
||||
| 'wallet';
|
||||
|
||||
export type WsPrivateSpotTopic = 'outboundAccountInfo'
|
||||
export type WsPrivateSpotTopic =
|
||||
| 'outboundAccountInfo'
|
||||
| 'executionReport'
|
||||
| 'ticketInfo';
|
||||
|
||||
export type WsPrivateTopic = WsPrivateInverseTopic
|
||||
export type WsPrivateTopic =
|
||||
| WsPrivateInverseTopic
|
||||
| WsPrivateUSDTPerpTopic
|
||||
| WsPrivateSpotTopic
|
||||
| string;
|
||||
@@ -135,7 +144,7 @@ export interface WSClientConfigurableOptions {
|
||||
restOptions?: any;
|
||||
requestOptions?: any;
|
||||
wsUrl?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
||||
livenet: boolean;
|
||||
@@ -147,8 +156,7 @@ export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
||||
pongTimeout: number;
|
||||
pingInterval: number;
|
||||
reconnectTimeout: number;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export const wsKeyInverse = 'inverse';
|
||||
export const wsKeyLinearPrivate = 'linearPrivate';
|
||||
@@ -157,29 +165,54 @@ export const wsKeySpotPrivate = 'spotPrivate';
|
||||
export const wsKeySpotPublic = 'spotPublic';
|
||||
|
||||
// This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets)
|
||||
export type WsKey = 'inverse' | 'linearPrivate' | 'linearPublic' | 'spotPrivate' | 'spotPublic';
|
||||
export type WsKey =
|
||||
| 'inverse'
|
||||
| 'linearPrivate'
|
||||
| 'linearPublic'
|
||||
| 'spotPrivate'
|
||||
| 'spotPublic';
|
||||
|
||||
const getLinearWsKeyForTopic = (topic: string): WsKey => {
|
||||
const privateLinearTopics = ['position', 'execution', 'order', 'stop_order', 'wallet'];
|
||||
const privateLinearTopics = [
|
||||
'position',
|
||||
'execution',
|
||||
'order',
|
||||
'stop_order',
|
||||
'wallet',
|
||||
];
|
||||
if (privateLinearTopics.includes(topic)) {
|
||||
return wsKeyLinearPrivate;
|
||||
}
|
||||
|
||||
return wsKeyLinearPublic;
|
||||
}
|
||||
};
|
||||
const getSpotWsKeyForTopic = (topic: string): WsKey => {
|
||||
const privateLinearTopics = ['position', 'execution', 'order', 'stop_order', 'outboundAccountInfo', 'executionReport', 'ticketInfo'];
|
||||
const privateLinearTopics = [
|
||||
'position',
|
||||
'execution',
|
||||
'order',
|
||||
'stop_order',
|
||||
'outboundAccountInfo',
|
||||
'executionReport',
|
||||
'ticketInfo',
|
||||
];
|
||||
|
||||
if (privateLinearTopics.includes(topic)) {
|
||||
return wsKeySpotPrivate;
|
||||
}
|
||||
|
||||
return wsKeySpotPublic;
|
||||
}
|
||||
};
|
||||
|
||||
export declare interface WebsocketClient {
|
||||
on(event: 'open' | 'reconnected', listener: ({ wsKey: WsKey, event: any }) => void): this;
|
||||
on(event: 'response' | 'update' | 'error', listener: (response: any) => void): this;
|
||||
on(
|
||||
event: 'open' | 'reconnected',
|
||||
listener: ({ wsKey: WsKey, event: any }) => void
|
||||
): this;
|
||||
on(
|
||||
event: 'response' | 'update' | 'error',
|
||||
listener: (response: any) => void
|
||||
): this;
|
||||
on(event: 'reconnect' | 'close', listener: ({ wsKey: WsKey }) => void): this;
|
||||
}
|
||||
|
||||
@@ -196,7 +229,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
private options: WebsocketClientOptions;
|
||||
private wsStore: WsStore;
|
||||
|
||||
constructor(options: WSClientConfigurableOptions, logger?: typeof DefaultLogger) {
|
||||
constructor(
|
||||
options: WSClientConfigurableOptions,
|
||||
logger?: typeof DefaultLogger
|
||||
) {
|
||||
super();
|
||||
|
||||
this.logger = logger || DefaultLogger;
|
||||
@@ -207,7 +243,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
pongTimeout: 1000,
|
||||
pingInterval: 10000,
|
||||
reconnectTimeout: 500,
|
||||
...options
|
||||
...options,
|
||||
};
|
||||
|
||||
if (!this.options.market) {
|
||||
@@ -215,13 +251,31 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this.isLinear()) {
|
||||
this.restClient = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions);
|
||||
this.restClient = new LinearClient(
|
||||
undefined,
|
||||
undefined,
|
||||
this.isLivenet(),
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
} else if (this.isSpot()) {
|
||||
// TODO: spot client
|
||||
this.restClient = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions);
|
||||
this.restClient = new LinearClient(
|
||||
undefined,
|
||||
undefined,
|
||||
this.isLivenet(),
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
this.connectPublic();
|
||||
} else {
|
||||
this.restClient = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions);
|
||||
this.restClient = new InverseClient(
|
||||
undefined,
|
||||
undefined,
|
||||
this.isLivenet(),
|
||||
this.options.restOptions,
|
||||
this.options.requestOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,10 +300,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
*/
|
||||
public subscribe(wsTopics: WsTopic[] | WsTopic) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
topics.forEach(topic => this.wsStore.addTopic(
|
||||
this.getWsKeyForTopic(topic),
|
||||
topic
|
||||
));
|
||||
topics.forEach((topic) =>
|
||||
this.wsStore.addTopic(this.getWsKeyForTopic(topic), topic)
|
||||
);
|
||||
|
||||
// attempt to send subscription topic per websocket
|
||||
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
||||
@@ -273,10 +326,9 @@ export class WebsocketClient extends EventEmitter {
|
||||
*/
|
||||
public unsubscribe(wsTopics: WsTopic[] | WsTopic) {
|
||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||
topics.forEach(topic => this.wsStore.deleteTopic(
|
||||
this.getWsKeyForTopic(topic),
|
||||
topic
|
||||
));
|
||||
topics.forEach((topic) =>
|
||||
this.wsStore.deleteTopic(this.getWsKeyForTopic(topic), topic)
|
||||
);
|
||||
|
||||
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
||||
// unsubscribe request only necessary if active connection exists
|
||||
@@ -303,7 +355,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this.isLinear()) {
|
||||
return [this.connect(wsKeyLinearPublic), this.connect(wsKeyLinearPrivate)];
|
||||
return [
|
||||
this.connect(wsKeyLinearPublic),
|
||||
this.connect(wsKeyLinearPrivate),
|
||||
];
|
||||
}
|
||||
|
||||
if (this.isSpot()) {
|
||||
@@ -342,12 +397,18 @@ export class WebsocketClient extends EventEmitter {
|
||||
private async connect(wsKey: WsKey): Promise<WebSocket | undefined> {
|
||||
try {
|
||||
if (this.wsStore.isWsOpen(wsKey)) {
|
||||
this.logger.error('Refused to connect to ws with existing active connection', { ...loggerCategory, 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, READY_STATE_CONNECTING)) {
|
||||
this.logger.error('Refused to connect to ws, connection attempt already active', { ...loggerCategory, wsKey })
|
||||
this.logger.error(
|
||||
'Refused to connect to ws, connection attempt already active',
|
||||
{ ...loggerCategory, wsKey }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -377,11 +438,17 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
switch (error.message) {
|
||||
case 'Unexpected server response: 401':
|
||||
this.logger.error(`${context} due to 401 authorization failure.`, { ...loggerCategory, wsKey });
|
||||
this.logger.error(`${context} due to 401 authorization failure.`, {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
this.logger.error(`{context} due to unexpected response error: ${error.msg}`, { ...loggerCategory, wsKey });
|
||||
this.logger.error(
|
||||
`{context} due to unexpected response error: ${error.msg}`,
|
||||
{ ...loggerCategory, wsKey }
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -392,23 +459,39 @@ export class WebsocketClient extends EventEmitter {
|
||||
private async getAuthParams(wsKey: WsKey): Promise<string> {
|
||||
const { key, secret } = this.options;
|
||||
|
||||
if (key && secret && wsKey !== wsKeyLinearPublic && wsKey !== wsKeySpotPublic) {
|
||||
this.logger.debug('Getting auth\'d request params', { ...loggerCategory, wsKey });
|
||||
if (
|
||||
key &&
|
||||
secret &&
|
||||
wsKey !== wsKeyLinearPublic &&
|
||||
wsKey !== wsKeySpotPublic
|
||||
) {
|
||||
this.logger.debug("Getting auth'd request params", {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
|
||||
const timeOffset = await this.restClient.getTimeOffset();
|
||||
const timeOffset = await this.restClient.fetchTimeOffset();
|
||||
|
||||
const params: any = {
|
||||
api_key: this.options.key,
|
||||
expires: (Date.now() + timeOffset + 5000)
|
||||
expires: Date.now() + timeOffset + 5000,
|
||||
};
|
||||
|
||||
params.signature = await signMessage('GET/realtime' + params.expires, secret);
|
||||
params.signature = await signMessage(
|
||||
'GET/realtime' + params.expires,
|
||||
secret
|
||||
);
|
||||
return '?' + serializeParams(params);
|
||||
|
||||
} else if (!key || !secret) {
|
||||
this.logger.warning('Connot authenticate websocket, either api or private keys missing.', { ...loggerCategory, wsKey });
|
||||
this.logger.warning(
|
||||
'Connot authenticate websocket, either api or private keys missing.',
|
||||
{ ...loggerCategory, wsKey }
|
||||
);
|
||||
} else {
|
||||
this.logger.debug('Starting public only websocket client.', { ...loggerCategory, wsKey });
|
||||
this.logger.debug('Starting public only websocket client.', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -421,7 +504,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.logger.info('Reconnecting to websocket', { ...loggerCategory, wsKey });
|
||||
this.logger.info('Reconnecting to websocket', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
this.connect(wsKey);
|
||||
}, connectionDelayMs);
|
||||
}
|
||||
@@ -433,7 +519,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' }));
|
||||
|
||||
this.wsStore.get(wsKey, true)!.activePongTimer = setTimeout(() => {
|
||||
this.logger.info('Pong timeout - closing socket to reconnect', { ...loggerCategory, wsKey });
|
||||
this.logger.info('Pong timeout - closing socket to reconnect', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
this.getWs(wsKey)?.close();
|
||||
}, this.options.pongTimeout);
|
||||
}
|
||||
@@ -470,7 +559,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
const wsMessage = JSON.stringify({
|
||||
op: 'subscribe',
|
||||
args: topics
|
||||
args: topics,
|
||||
});
|
||||
|
||||
this.tryWsSend(wsKey, wsMessage);
|
||||
@@ -485,7 +574,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
const wsMessage = JSON.stringify({
|
||||
op: 'unsubscribe',
|
||||
args: topics
|
||||
args: topics,
|
||||
});
|
||||
|
||||
this.tryWsSend(wsKey, wsMessage);
|
||||
@@ -493,38 +582,62 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
private tryWsSend(wsKey: WsKey, wsMessage: string) {
|
||||
try {
|
||||
this.logger.silly(`Sending upstream ws message: `, { ...loggerCategory, wsMessage, wsKey });
|
||||
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');
|
||||
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`);
|
||||
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 });
|
||||
this.logger.error(`Failed to send WS message`, {
|
||||
...loggerCategory,
|
||||
wsMessage,
|
||||
wsKey,
|
||||
exception: e,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private connectToWsUrl(url: string, wsKey: WsKey): WebSocket {
|
||||
this.logger.silly(`Opening WS connection to URL: ${url}`, { ...loggerCategory, wsKey })
|
||||
this.logger.silly(`Opening WS connection to URL: ${url}`, {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
|
||||
const agent = this.options.requestOptions?.agent;
|
||||
const ws = new WebSocket(url, undefined, agent ? { agent } : undefined);
|
||||
ws.onopen = event => this.onWsOpen(event, wsKey);
|
||||
ws.onmessage = event => this.onWsMessage(event, wsKey);
|
||||
ws.onerror = event => this.onWsError(event, wsKey);
|
||||
ws.onclose = event => this.onWsClose(event, wsKey);
|
||||
ws.onopen = (event) => this.onWsOpen(event, wsKey);
|
||||
ws.onmessage = (event) => this.onWsMessage(event, wsKey);
|
||||
ws.onerror = (event) => this.onWsError(event, wsKey);
|
||||
ws.onclose = (event) => this.onWsClose(event, wsKey);
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
private onWsOpen(event, wsKey: WsKey) {
|
||||
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) {
|
||||
this.logger.info('Websocket connected', { ...loggerCategory, wsKey, livenet: this.isLivenet(), linear: this.isLinear(), spot: this.isSpot() });
|
||||
this.logger.info('Websocket connected', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
livenet: this.isLivenet(),
|
||||
linear: this.isLinear(),
|
||||
spot: this.isSpot(),
|
||||
});
|
||||
this.emit('open', { wsKey, event });
|
||||
} else if (this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING)) {
|
||||
} else if (
|
||||
this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING)
|
||||
) {
|
||||
this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey });
|
||||
this.emit('reconnected', { wsKey, event });
|
||||
}
|
||||
@@ -547,16 +660,26 @@ export class WebsocketClient extends EventEmitter {
|
||||
// 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);
|
||||
const msg = JSON.parse((event && event.data) || event);
|
||||
if ('success' in msg || msg?.pong) {
|
||||
this.onWsMessageResponse(msg, wsKey);
|
||||
} else if (msg.topic) {
|
||||
this.onWsMessageUpdate(msg);
|
||||
} else {
|
||||
this.logger.warning('Got unhandled ws message', { ...loggerCategory, message: msg, event, wsKey});
|
||||
this.logger.warning('Got unhandled ws message', {
|
||||
...loggerCategory,
|
||||
message: msg,
|
||||
event,
|
||||
wsKey,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error('Failed to parse ws event message', { ...loggerCategory, error: e, event, wsKey})
|
||||
this.logger.error('Failed to parse ws event message', {
|
||||
...loggerCategory,
|
||||
error: e,
|
||||
event,
|
||||
wsKey,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,7 +691,10 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
|
||||
private onWsClose(event, wsKey: WsKey) {
|
||||
this.logger.info('Websocket connection closed', { ...loggerCategory, wsKey});
|
||||
this.logger.info('Websocket connection closed', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
|
||||
if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) {
|
||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
|
||||
@@ -606,7 +732,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
|
||||
const networkKey = this.isLivenet() ? 'livenet' : 'testnet';
|
||||
// TODO: reptitive
|
||||
if (this.isLinear() || wsKey.startsWith('linear')){
|
||||
if (this.isLinear() || wsKey.startsWith('linear')) {
|
||||
if (wsKey === wsKeyLinearPublic) {
|
||||
return linearEndpoints.public[networkKey];
|
||||
}
|
||||
@@ -615,11 +741,14 @@ export class WebsocketClient extends EventEmitter {
|
||||
return linearEndpoints.private[networkKey];
|
||||
}
|
||||
|
||||
this.logger.error('Unhandled linear wsKey: ', { ...loggerCategory, wsKey });
|
||||
this.logger.error('Unhandled linear wsKey: ', {
|
||||
...loggerCategory,
|
||||
wsKey,
|
||||
});
|
||||
return linearEndpoints[networkKey];
|
||||
}
|
||||
|
||||
if (this.isSpot() || wsKey.startsWith('spot')){
|
||||
if (this.isSpot() || wsKey.startsWith('spot')) {
|
||||
if (wsKey === wsKeySpotPublic) {
|
||||
return spotEndpoints.public[networkKey];
|
||||
}
|
||||
@@ -641,13 +770,15 @@ export class WebsocketClient extends EventEmitter {
|
||||
return wsKeyInverse;
|
||||
}
|
||||
if (this.isLinear()) {
|
||||
return getLinearWsKeyForTopic(topic)
|
||||
return getLinearWsKeyForTopic(topic);
|
||||
}
|
||||
return getSpotWsKeyForTopic(topic);
|
||||
}
|
||||
|
||||
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`);
|
||||
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`
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: persistance for subbed topics. Look at ftx-api implementation.
|
||||
@@ -656,14 +787,17 @@ export class WebsocketClient extends EventEmitter {
|
||||
throw this.wrongMarketError('spot');
|
||||
}
|
||||
|
||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
||||
topic: 'trade',
|
||||
event: 'sub',
|
||||
symbol,
|
||||
params: {
|
||||
binary: !!binary,
|
||||
}
|
||||
}));
|
||||
return this.tryWsSend(
|
||||
wsKeySpotPublic,
|
||||
JSON.stringify({
|
||||
topic: 'trade',
|
||||
event: 'sub',
|
||||
symbol,
|
||||
params: {
|
||||
binary: !!binary,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public subscribePublicSpotTradingPair(symbol: string, binary?: boolean) {
|
||||
@@ -671,35 +805,50 @@ export class WebsocketClient extends EventEmitter {
|
||||
throw this.wrongMarketError('spot');
|
||||
}
|
||||
|
||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
||||
symbol,
|
||||
topic: 'realtimes',
|
||||
event: 'sub',
|
||||
params: {
|
||||
binary: !!binary,
|
||||
},
|
||||
}));
|
||||
return this.tryWsSend(
|
||||
wsKeySpotPublic,
|
||||
JSON.stringify({
|
||||
symbol,
|
||||
topic: 'realtimes',
|
||||
event: 'sub',
|
||||
params: {
|
||||
binary: !!binary,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public subscribePublicSpotV1Kline(symbol: string, candleSize: KlineInterval, binary?: boolean) {
|
||||
public subscribePublicSpotV1Kline(
|
||||
symbol: string,
|
||||
candleSize: KlineInterval,
|
||||
binary?: boolean
|
||||
) {
|
||||
if (!this.isSpot()) {
|
||||
throw this.wrongMarketError('spot');
|
||||
}
|
||||
|
||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
||||
symbol,
|
||||
topic: 'kline_' + candleSize,
|
||||
event: 'sub',
|
||||
params: {
|
||||
binary: !!binary,
|
||||
},
|
||||
}));
|
||||
return this.tryWsSend(
|
||||
wsKeySpotPublic,
|
||||
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}}');
|
||||
public subscribePublicSpotOrderbook(symbol: string, depth: 'full' | 'merge' | 'delta', dumpScale?: number, binary?: boolean) {
|
||||
public subscribePublicSpotOrderbook(
|
||||
symbol: string,
|
||||
depth: 'full' | 'merge' | 'delta',
|
||||
dumpScale?: number,
|
||||
binary?: boolean
|
||||
) {
|
||||
if (!this.isSpot()) {
|
||||
throw this.wrongMarketError('spot');
|
||||
}
|
||||
@@ -709,7 +858,7 @@ export class WebsocketClient extends EventEmitter {
|
||||
case 'full': {
|
||||
topic = 'depth';
|
||||
break;
|
||||
};
|
||||
}
|
||||
case 'merge': {
|
||||
topic = 'mergedDepth';
|
||||
if (!dumpScale) {
|
||||
@@ -736,5 +885,4 @@ export class WebsocketClient extends EventEmitter {
|
||||
}
|
||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify(msg));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user