Merge pull request #150 from tiagosiebler/deprecateReqWrapper
v2.2.0: Extensive integration tests, revise base REST client, fix Spot REST client, type improvements
This commit is contained in:
7581
package-lock.json
generated
7581
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "2.1.10",
|
"version": "2.2.0",
|
||||||
"description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
|
"description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
|
|||||||
@@ -9,3 +9,34 @@ export const positionTpSlModeEnum = {
|
|||||||
/** Partial take profit/stop loss mode (multiple TP and SL orders can be placed, covering portions of the position) */
|
/** Partial take profit/stop loss mode (multiple TP and SL orders can be placed, covering portions of the position) */
|
||||||
Partial: 'Partial',
|
Partial: 'Partial',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const API_ERROR_CODE = {
|
||||||
|
BALANCE_INSUFFICIENT_SPOT: -1131,
|
||||||
|
ORDER_NOT_FOUND_OR_TOO_LATE_SPOT: -2013,
|
||||||
|
/** This could mean bad request, incorrect value types or even incorrect/missing values */
|
||||||
|
PARAMS_MISSING_OR_WRONG: 10001,
|
||||||
|
ORDER_NOT_FOUND_OR_TOO_LATE: 20001,
|
||||||
|
POSITION_STATUS_NOT_NORMAL: 30013,
|
||||||
|
CANNOT_SET_TRADING_STOP_FOR_ZERO_POS: 30024,
|
||||||
|
/** Seen when placing an order */
|
||||||
|
INSUFFICIENT_BALANCE_FOR_ORDER_COST: 30031,
|
||||||
|
POSITION_IDX_NOT_MATCH_POSITION_MODE: 30041,
|
||||||
|
/** Seen if a conditional order is too large */
|
||||||
|
INSUFFICIENT_BALANCE: 30042,
|
||||||
|
/** E.g. trying to change position margin while on cross */
|
||||||
|
POSITION_IS_CROSS_MARGIN: 30056,
|
||||||
|
POSITION_MODE_NOT_MODIFIED: 30083,
|
||||||
|
ISOLATED_NOT_MODIFIED: 30084,
|
||||||
|
RISK_LIMIT_NOT_EXISTS: 30090,
|
||||||
|
LEVERAGE_NOT_MODIFIED: 34036,
|
||||||
|
SAME_SLTP_MODE: 37002,
|
||||||
|
ORDER_NOT_FOUND_OR_TOO_LATE_LINEAR: 130010,
|
||||||
|
ORDER_COST_NOT_AVAILABLE: 130021,
|
||||||
|
CANNOT_SET_LINEAR_TRADING_STOP_FOR_ZERO_POS: 130024,
|
||||||
|
ISOLATED_NOT_MODIFIED_LINEAR: 130056,
|
||||||
|
POSITION_SIZE_IS_ZERO: 130057,
|
||||||
|
AUTO_ADD_MARGIN_NOT_MODIFIED: 130060,
|
||||||
|
INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080,
|
||||||
|
SAME_SLTP_MODE_LINEAR: 130150,
|
||||||
|
RISK_ID_NOT_MODIFIED: 134026,
|
||||||
|
} as const;
|
||||||
|
|||||||
20
src/index.ts
20
src/index.ts
@@ -1,10 +1,10 @@
|
|||||||
export * from "./inverse-client";
|
export * from './inverse-client';
|
||||||
export * from "./inverse-futures-client";
|
export * from './inverse-futures-client';
|
||||||
export * from "./linear-client";
|
export * from './linear-client';
|
||||||
export * from "./spot-client";
|
export * from './spot-client';
|
||||||
export * from "./websocket-client";
|
export * from './websocket-client';
|
||||||
export * from "./logger";
|
export * from './logger';
|
||||||
export * from "./types/shared";
|
export * from './types/shared';
|
||||||
export * from "./types/spot";
|
export * from './types/spot';
|
||||||
export * from "./util/WsStore";
|
export * from './util/WsStore';
|
||||||
export * from "./constants/enum";
|
export * from './constants/enum';
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
import {
|
||||||
import RequestWrapper from './util/requestWrapper';
|
getRestBaseUrl,
|
||||||
import SharedEndpoints from './shared-endpoints';
|
RestClientOptions,
|
||||||
import { SymbolFromLimitParam, SymbolIntervalFromLimitParam, SymbolParam } from './types/shared';
|
REST_CLIENT_TYPE_ENUM,
|
||||||
|
} from './util/requestUtils';
|
||||||
export class InverseClient extends SharedEndpoints {
|
import {
|
||||||
protected requestWrapper: RequestWrapper;
|
APIResponseWithTime,
|
||||||
|
AssetExchangeRecordsReq,
|
||||||
|
CoinParam,
|
||||||
|
SymbolInfo,
|
||||||
|
SymbolIntervalFromLimitParam,
|
||||||
|
SymbolLimitParam,
|
||||||
|
SymbolParam,
|
||||||
|
SymbolPeriodLimitParam,
|
||||||
|
WalletFundRecordsReq,
|
||||||
|
WithdrawRecordsReq,
|
||||||
|
} from './types/shared';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
export class InverseClient extends BaseRestClient {
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the inverse REST API client.
|
* @public Creates an instance of the inverse REST API client.
|
||||||
*
|
*
|
||||||
@@ -23,56 +35,149 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
restClientOptions: RestClientOptions = {},
|
restClientOptions: RestClientOptions = {},
|
||||||
requestOptions: AxiosRequestConfig = {}
|
requestOptions: AxiosRequestConfig = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super(
|
||||||
|
|
||||||
this.requestWrapper = new RequestWrapper(
|
|
||||||
key,
|
key,
|
||||||
secret,
|
secret,
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
getRestBaseUrl(useLivenet, restClientOptions),
|
||||||
restClientOptions,
|
restClientOptions,
|
||||||
requestOptions
|
requestOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM.inverse
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Market Data Endpoints
|
* Market Data Endpoints
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('v2/public/kline/list', params);
|
return this.get('v2/public/orderBook/L2', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/kline/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use getTickers() instead
|
* Get latest information for symbol
|
||||||
*/
|
*/
|
||||||
getLatestInformation(params?: Partial<SymbolParam>): GenericAPIResponse {
|
getTickers(
|
||||||
return this.getTickers(params);
|
params?: Partial<SymbolParam>
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/tickers', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTrades(params: SymbolLimitParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/trading-records', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSymbols(): Promise<APIResponseWithTime<SymbolInfo[]>> {
|
||||||
|
return this.get('v2/public/symbols');
|
||||||
|
}
|
||||||
|
|
||||||
|
getMarkPriceKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/mark-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexPriceKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/index-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPremiumIndexKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/premium-index-kline', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated use getTrades() instead
|
*
|
||||||
|
* Market Data : Advanced
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
getPublicTradingRecords(params: SymbolFromLimitParam): GenericAPIResponse {
|
|
||||||
return this.getTrades(params);
|
getOpenInterest(
|
||||||
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/open-interest', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrades(params: SymbolFromLimitParam): GenericAPIResponse {
|
getLatestBigDeal(
|
||||||
return this.requestWrapper.get('v2/public/trading-records', params);
|
params: SymbolLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/big-deal', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMarkPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getLongShortRatio(
|
||||||
return this.requestWrapper.get('v2/public/mark-price-kline', params);
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/account-ratio', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
/**
|
||||||
return this.requestWrapper.get('v2/public/index-price-kline', params);
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getApiKeyInfo(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/account/api-key');
|
||||||
}
|
}
|
||||||
|
|
||||||
getPremiumIndexKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
/**
|
||||||
return this.requestWrapper.get('v2/public/premium-index-kline', params);
|
*
|
||||||
|
* Wallet Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getWalletBalance(
|
||||||
|
params?: Partial<CoinParam>
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/balance', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWalletFundRecords(
|
||||||
|
params?: WalletFundRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/fund/records', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWithdrawRecords(
|
||||||
|
params?: WithdrawRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/withdraw/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetExchangeRecords(
|
||||||
|
params?: AssetExchangeRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/exchange-order/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime<{}>> {
|
||||||
|
return this.get('v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiAnnouncements(): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/announcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,8 +204,8 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
sl_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
sl_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
||||||
close_on_trigger?: boolean;
|
close_on_trigger?: boolean;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/order/create', orderRequest);
|
return this.postPrivate('v2/private/order/create', orderRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveOrderList(params: {
|
getActiveOrderList(params: {
|
||||||
@@ -109,20 +214,22 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
direction?: string;
|
direction?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/order/list', params);
|
return this.getPrivate('v2/private/order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelActiveOrder(params: {
|
cancelActiveOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/order/cancel', params);
|
return this.postPrivate('v2/private/order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllActiveOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllActiveOrders(
|
||||||
return this.requestWrapper.post('v2/private/order/cancelAll', params);
|
params: SymbolParam
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('v2/private/order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceActiveOrder(params: {
|
replaceActiveOrder(params: {
|
||||||
@@ -133,18 +240,18 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
p_r_price?: string;
|
p_r_price?: string;
|
||||||
take_profit?: number;
|
take_profit?: number;
|
||||||
stop_loss?: number;
|
stop_loss?: number;
|
||||||
tp_trigger_by?:string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?:string;
|
sl_trigger_by?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/order/replace', params);
|
return this.postPrivate('v2/private/order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryActiveOrder(params: {
|
queryActiveOrder(params: {
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/order', params);
|
return this.getPrivate('v2/private/order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,30 +270,33 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
trigger_by?: string;
|
trigger_by?: string;
|
||||||
close_on_trigger?: boolean;
|
close_on_trigger?: boolean;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/stop-order/create', params);
|
return this.postPrivate('v2/private/stop-order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** get conditional order list. This may see delays, use queryConditionalOrder() for real-time queries */
|
||||||
getConditionalOrder(params: {
|
getConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_status?: string;
|
stop_order_status?: string;
|
||||||
direction?: string;
|
direction?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/stop-order/list', params);
|
return this.getPrivate('v2/private/stop-order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelConditionalOrder(params: {
|
cancelConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/stop-order/cancel', params);
|
return this.postPrivate('v2/private/stop-order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllConditionalOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllConditionalOrders(
|
||||||
return this.requestWrapper.post('v2/private/stop-order/cancelAll', params);
|
params: SymbolParam
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('v2/private/stop-order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceConditionalOrder(params: {
|
replaceConditionalOrder(params: {
|
||||||
@@ -196,45 +306,33 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
p_r_qty?: number;
|
p_r_qty?: number;
|
||||||
p_r_price?: string;
|
p_r_price?: string;
|
||||||
p_r_trigger_price?: string;
|
p_r_trigger_price?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/stop-order/replace', params);
|
return this.postPrivate('v2/private/stop-order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryConditionalOrder(params: {
|
queryConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/stop-order', params);
|
return this.getPrivate('v2/private/stop-order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position
|
* Position
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
getPosition(
|
||||||
* @deprecated use getPosition() instead
|
params?: Partial<SymbolParam>
|
||||||
*/
|
): Promise<APIResponseWithTime<any>> {
|
||||||
getUserLeverage(): GenericAPIResponse {
|
return this.getPrivate('v2/private/position/list', params);
|
||||||
return this.requestWrapper.get('user/leverage');
|
|
||||||
}
|
|
||||||
|
|
||||||
getPosition(params?: Partial<SymbolParam>): GenericAPIResponse {
|
|
||||||
return this.requestWrapper.get('v2/private/position/list', params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use getPosition() instead
|
|
||||||
*/
|
|
||||||
getPositions(): GenericAPIResponse {
|
|
||||||
return this.requestWrapper.get('position/list');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changePositionMargin(params: {
|
changePositionMargin(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
margin: string;
|
margin: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('position/change-position-margin', params);
|
return this.postPrivate('position/change-position-margin', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradingStop(params: {
|
setTradingStop(params: {
|
||||||
@@ -245,23 +343,16 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
tp_trigger_by?: string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
new_trailing_active?: number;
|
new_trailing_active?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/position/trading-stop', params);
|
return this.postPrivate('v2/private/position/trading-stop', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLeverage(params: {
|
setUserLeverage(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
leverage: number;
|
leverage: number;
|
||||||
leverage_only?: boolean;
|
leverage_only?: boolean;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/position/leverage/save', params);
|
return this.postPrivate('v2/private/position/leverage/save', params);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use setUserLeverage() instead
|
|
||||||
*/
|
|
||||||
changeUserLeverage(params: any): GenericAPIResponse {
|
|
||||||
return this.setUserLeverage(params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTradeRecords(params: {
|
getTradeRecords(params: {
|
||||||
@@ -271,8 +362,8 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
order?: string;
|
order?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/execution/list', params);
|
return this.getPrivate('v2/private/execution/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosedPnl(params: {
|
getClosedPnl(params: {
|
||||||
@@ -282,22 +373,15 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
exec_type?: string;
|
exec_type?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params);
|
return this.getPrivate('v2/private/trade/closed-pnl/list', params);
|
||||||
}
|
|
||||||
|
|
||||||
setPositionMode(params: {
|
|
||||||
symbol: string;
|
|
||||||
mode: 0 | 3;
|
|
||||||
}): GenericAPIResponse {
|
|
||||||
return this.requestWrapper.post('v2/private/position/switch-mode', params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSlTpPositionMode(params: {
|
setSlTpPositionMode(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
tp_sl_mode: 'Full' | 'Partial';
|
tp_sl_mode: 'Full' | 'Partial';
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/tpsl/switch-mode', params);
|
return this.postPrivate('v2/private/tpsl/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMarginType(params: {
|
setMarginType(params: {
|
||||||
@@ -305,52 +389,46 @@ export class InverseClient extends SharedEndpoints {
|
|||||||
is_isolated: boolean;
|
is_isolated: boolean;
|
||||||
buy_leverage: number;
|
buy_leverage: number;
|
||||||
sell_leverage: number;
|
sell_leverage: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('v2/private/position/switch-isolated', params);
|
return this.postPrivate('v2/private/position/switch-isolated', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Risk Limit
|
* Risk Limit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getRiskLimitList(): GenericAPIResponse {
|
getRiskLimitList(): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('open-api/wallet/risk-limit/list');
|
return this.getPrivate('open-api/wallet/risk-limit/list');
|
||||||
}
|
}
|
||||||
|
|
||||||
setRiskLimit(params: {
|
setRiskLimit(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
risk_id: string;
|
risk_id: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('open-api/wallet/risk-limit', params);
|
return this.postPrivate('open-api/wallet/risk-limit', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Funding
|
* Funding
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLastFundingRate(params: SymbolParam): GenericAPIResponse {
|
getLastFundingRate(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params);
|
return this.get('v2/public/funding/prev-funding-rate', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyLastFundingFee(params: SymbolParam): GenericAPIResponse {
|
getMyLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/funding/prev-funding', params);
|
return this.getPrivate('v2/private/funding/prev-funding', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPredictedFunding(params: SymbolParam): GenericAPIResponse {
|
getPredictedFunding(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/funding/predicted-funding', params);
|
return this.getPrivate('v2/private/funding/predicted-funding', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LCP Info
|
* LCP Info
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLcpInfo(params: SymbolParam): GenericAPIResponse {
|
getLcpInfo(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/account/lcp', params);
|
return this.getPrivate('v2/private/account/lcp', params);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//API Key Info
|
|
||||||
getAPIKeyInfo(): GenericAPIResponse {
|
|
||||||
return this.requestWrapper.get('v2/private/account/api-key');
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
import {
|
||||||
import RequestWrapper from './util/requestWrapper';
|
getRestBaseUrl,
|
||||||
import SharedEndpoints from './shared-endpoints';
|
RestClientOptions,
|
||||||
import { SymbolFromLimitParam, SymbolIntervalFromLimitParam, SymbolParam } from './types/shared';
|
REST_CLIENT_TYPE_ENUM,
|
||||||
|
} from './util/requestUtils';
|
||||||
export class InverseFuturesClient extends SharedEndpoints {
|
import {
|
||||||
protected requestWrapper: RequestWrapper;
|
APIResponseWithTime,
|
||||||
|
AssetExchangeRecordsReq,
|
||||||
|
CoinParam,
|
||||||
|
SymbolInfo,
|
||||||
|
SymbolIntervalFromLimitParam,
|
||||||
|
SymbolLimitParam,
|
||||||
|
SymbolParam,
|
||||||
|
SymbolPeriodLimitParam,
|
||||||
|
WalletFundRecordsReq,
|
||||||
|
WithdrawRecordsReq,
|
||||||
|
} from './types/shared';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
export class InverseFuturesClient extends BaseRestClient {
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the inverse futures REST API client.
|
* @public Creates an instance of the inverse futures REST API client.
|
||||||
*
|
*
|
||||||
@@ -23,45 +35,152 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
restClientOptions: RestClientOptions = {},
|
restClientOptions: RestClientOptions = {},
|
||||||
requestOptions: AxiosRequestConfig = {}
|
requestOptions: AxiosRequestConfig = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super(
|
||||||
|
|
||||||
this.requestWrapper = new RequestWrapper(
|
|
||||||
key,
|
key,
|
||||||
secret,
|
secret,
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
getRestBaseUrl(useLivenet, restClientOptions),
|
||||||
restClientOptions,
|
restClientOptions,
|
||||||
requestOptions
|
requestOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM.inverseFutures
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Market Data Endpoints
|
* Market Data Endpoints
|
||||||
* Note: These are currently the same as the inverse client
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('v2/public/kline/list', params);
|
return this.get('v2/public/orderBook/L2', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/kline/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get latest information for symbol
|
||||||
|
*/
|
||||||
|
getTickers(
|
||||||
|
params?: Partial<SymbolParam>
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/tickers', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public trading records
|
* Public trading records
|
||||||
*/
|
*/
|
||||||
getTrades(params: SymbolFromLimitParam): GenericAPIResponse {
|
getTrades(params: SymbolLimitParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('v2/public/trading-records', params);
|
return this.get('v2/public/trading-records', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMarkPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getSymbols(): Promise<APIResponseWithTime<SymbolInfo[]>> {
|
||||||
return this.requestWrapper.get('v2/public/mark-price-kline', params);
|
return this.get('v2/public/symbols');
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getMarkPriceKline(
|
||||||
return this.requestWrapper.get('v2/public/index-price-kline', params);
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/mark-price-kline', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPremiumIndexKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getIndexPriceKline(
|
||||||
return this.requestWrapper.get('v2/public/premium-index-kline', params);
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/index-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPremiumIndexKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/premium-index-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data : Advanced
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getOpenInterest(
|
||||||
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/open-interest', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestBigDeal(
|
||||||
|
params: SymbolLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/big-deal', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLongShortRatio(
|
||||||
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/account-ratio', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getApiKeyInfo(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/account/api-key');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Wallet Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getWalletBalance(
|
||||||
|
params?: Partial<CoinParam>
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/balance', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWalletFundRecords(
|
||||||
|
params?: WalletFundRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/fund/records', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWithdrawRecords(
|
||||||
|
params?: WithdrawRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/withdraw/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetExchangeRecords(
|
||||||
|
params?: AssetExchangeRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/exchange-order/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime<{}>> {
|
||||||
|
return this.get('v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiAnnouncements(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.get('v2/public/announcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,8 +205,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
reduce_only?: boolean;
|
reduce_only?: boolean;
|
||||||
close_on_trigger?: boolean;
|
close_on_trigger?: boolean;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/order/create', orderRequest);
|
return this.postPrivate('futures/private/order/create', orderRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveOrderList(params: {
|
getActiveOrderList(params: {
|
||||||
@@ -96,20 +215,22 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
direction?: string;
|
direction?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/order/list', params);
|
return this.getPrivate('futures/private/order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelActiveOrder(params: {
|
cancelActiveOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/order/cancel', params);
|
return this.postPrivate('futures/private/order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllActiveOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllActiveOrders(
|
||||||
return this.requestWrapper.post('futures/private/order/cancelAll', params);
|
params: SymbolParam
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('futures/private/order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceActiveOrder(params: {
|
replaceActiveOrder(params: {
|
||||||
@@ -118,16 +239,16 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
symbol: string;
|
symbol: string;
|
||||||
p_r_qty?: string;
|
p_r_qty?: string;
|
||||||
p_r_price?: string;
|
p_r_price?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/order/replace', params);
|
return this.postPrivate('futures/private/order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryActiveOrder(params: {
|
queryActiveOrder(params: {
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/order', params);
|
return this.getPrivate('futures/private/order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,8 +267,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
trigger_by?: string;
|
trigger_by?: string;
|
||||||
close_on_trigger?: boolean;
|
close_on_trigger?: boolean;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/stop-order/create', params);
|
return this.postPrivate('futures/private/stop-order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConditionalOrder(params: {
|
getConditionalOrder(params: {
|
||||||
@@ -156,20 +277,22 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
direction?: string;
|
direction?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
cursor?: string;
|
cursor?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/stop-order/list', params);
|
return this.getPrivate('futures/private/stop-order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelConditionalOrder(params: {
|
cancelConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/stop-order/cancel', params);
|
return this.postPrivate('futures/private/stop-order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllConditionalOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllConditionalOrders(
|
||||||
return this.requestWrapper.post('futures/private/stop-order/cancelAll', params);
|
params: SymbolParam
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('futures/private/stop-order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceConditionalOrder(params: {
|
replaceConditionalOrder(params: {
|
||||||
@@ -179,35 +302,39 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
p_r_qty?: number;
|
p_r_qty?: number;
|
||||||
p_r_price?: string;
|
p_r_price?: string;
|
||||||
p_r_trigger_price?: string;
|
p_r_trigger_price?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/stop-order/replace', params);
|
return this.postPrivate('futures/private/stop-order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryConditionalOrder(params: {
|
queryConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/stop-order', params);
|
return this.getPrivate('futures/private/stop-order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position
|
* Position
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get position list
|
* Get position list
|
||||||
*/
|
*/
|
||||||
getPosition(params?: Partial<SymbolParam>): GenericAPIResponse {
|
getPosition(
|
||||||
return this.requestWrapper.get('futures/private/position/list', params);
|
params?: Partial<SymbolParam>
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('futures/private/position/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
changePositionMargin(params: {
|
changePositionMargin(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
margin: string;
|
margin: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/position/change-position-margin', params);
|
return this.postPrivate(
|
||||||
|
'futures/private/position/change-position-margin',
|
||||||
|
params
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradingStop(params: {
|
setTradingStop(params: {
|
||||||
@@ -218,16 +345,16 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
tp_trigger_by?: string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
new_trailing_active?: number;
|
new_trailing_active?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/position/trading-stop', params);
|
return this.postPrivate('futures/private/position/trading-stop', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLeverage(params: {
|
setUserLeverage(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
buy_leverage: number;
|
buy_leverage: number;
|
||||||
sell_leverage: number;
|
sell_leverage: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/position/leverage/save', params);
|
return this.postPrivate('futures/private/position/leverage/save', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,8 +363,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
setPositionMode(params: {
|
setPositionMode(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
mode: number;
|
mode: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/position/switch-mode', params);
|
return this.postPrivate('futures/private/position/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,8 +375,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
is_isolated: boolean;
|
is_isolated: boolean;
|
||||||
buy_leverage: number;
|
buy_leverage: number;
|
||||||
sell_leverage: number;
|
sell_leverage: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('futures/private/position/switch-isolated', params);
|
return this.postPrivate('futures/private/position/switch-isolated', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTradeRecords(params: {
|
getTradeRecords(params: {
|
||||||
@@ -259,8 +386,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
order?: string;
|
order?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/execution/list', params);
|
return this.getPrivate('futures/private/execution/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosedPnl(params: {
|
getClosedPnl(params: {
|
||||||
@@ -270,8 +397,8 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
exec_type?: string;
|
exec_type?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('futures/private/trade/closed-pnl/list', params);
|
return this.getPrivate('futures/private/trade/closed-pnl/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,38 +408,38 @@ export class InverseFuturesClient extends SharedEndpoints {
|
|||||||
/**
|
/**
|
||||||
* Risk Limit
|
* Risk Limit
|
||||||
*/
|
*/
|
||||||
getRiskLimitList(): GenericAPIResponse {
|
getRiskLimitList(): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('open-api/wallet/risk-limit/list');
|
return this.getPrivate('open-api/wallet/risk-limit/list');
|
||||||
}
|
}
|
||||||
|
|
||||||
setRiskLimit(params: {
|
setRiskLimit(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
risk_id: string;
|
risk_id: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('open-api/wallet/risk-limit', params);
|
return this.postPrivate('open-api/wallet/risk-limit', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Funding
|
* Funding
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLastFundingRate(params: SymbolParam): GenericAPIResponse {
|
getLastFundingRate(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params);
|
return this.get('v2/public/funding/prev-funding-rate', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyLastFundingFee(params: SymbolParam): GenericAPIResponse {
|
getMyLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/funding/prev-funding', params);
|
return this.getPrivate('v2/private/funding/prev-funding', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPredictedFunding(params: SymbolParam): GenericAPIResponse {
|
getPredictedFunding(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/funding/predicted-funding', params);
|
return this.getPrivate('v2/private/funding/predicted-funding', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LCP Info
|
* LCP Info
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getLcpInfo(params: SymbolParam): GenericAPIResponse {
|
getLcpInfo(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('v2/private/account/lcp', params);
|
return this.getPrivate('v2/private/account/lcp', params);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import {
|
import {
|
||||||
GenericAPIResponse,
|
|
||||||
getRestBaseUrl,
|
getRestBaseUrl,
|
||||||
RestClientOptions,
|
RestClientOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM,
|
||||||
} from './util/requestUtils';
|
} from './util/requestUtils';
|
||||||
import RequestWrapper from './util/requestWrapper';
|
|
||||||
import SharedEndpoints from './shared-endpoints';
|
|
||||||
import {
|
import {
|
||||||
|
APIResponse,
|
||||||
|
APIResponseWithTime,
|
||||||
|
AssetExchangeRecordsReq,
|
||||||
|
CoinParam,
|
||||||
|
SymbolInfo,
|
||||||
SymbolIntervalFromLimitParam,
|
SymbolIntervalFromLimitParam,
|
||||||
SymbolLimitParam,
|
SymbolLimitParam,
|
||||||
SymbolParam,
|
SymbolParam,
|
||||||
|
SymbolPeriodLimitParam,
|
||||||
|
WalletFundRecordsReq,
|
||||||
|
WithdrawRecordsReq,
|
||||||
} from './types/shared';
|
} from './types/shared';
|
||||||
import { linearPositionModeEnum, positionTpSlModeEnum } from './constants/enum';
|
import { linearPositionModeEnum, positionTpSlModeEnum } from './constants/enum';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
export class LinearClient extends SharedEndpoints {
|
export class LinearClient extends BaseRestClient {
|
||||||
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} key - your API key
|
||||||
* @param {string} secret - your API secret
|
* @param {string} secret - your API secret
|
||||||
@@ -32,54 +37,153 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
restClientOptions: RestClientOptions = {},
|
restClientOptions: RestClientOptions = {},
|
||||||
requestOptions: AxiosRequestConfig = {}
|
requestOptions: AxiosRequestConfig = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super(
|
||||||
|
|
||||||
this.requestWrapper = new RequestWrapper(
|
|
||||||
key,
|
key,
|
||||||
secret,
|
secret,
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
getRestBaseUrl(useLivenet, restClientOptions),
|
||||||
restClientOptions,
|
restClientOptions,
|
||||||
requestOptions
|
requestOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM.linear
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const timeRes = await this.getServerTime();
|
||||||
|
return Number(timeRes.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Market Data Endpoints
|
* Market Data Endpoints
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('public/linear/kline', params);
|
return this.get('v2/public/orderBook/L2', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrades(params: SymbolLimitParam): GenericAPIResponse {
|
getKline(
|
||||||
return this.requestWrapper.get(
|
params: SymbolIntervalFromLimitParam
|
||||||
'public/linear/recent-trading-records',
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
params
|
return this.get('public/linear/kline', params);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastFundingRate(params: SymbolParam): GenericAPIResponse {
|
/**
|
||||||
return this.requestWrapper.get(
|
* Get latest information for symbol
|
||||||
'public/linear/funding/prev-funding-rate',
|
*/
|
||||||
params
|
getTickers(
|
||||||
);
|
params?: Partial<SymbolParam>
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/tickers', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMarkPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getTrades(params: SymbolLimitParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('public/linear/mark-price-kline', params);
|
return this.get('public/linear/recent-trading-records', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexPriceKline(params: SymbolIntervalFromLimitParam): GenericAPIResponse {
|
getSymbols(): Promise<APIResponse<SymbolInfo[]>> {
|
||||||
return this.requestWrapper.get('public/linear/index-price-kline', params);
|
return this.get('v2/public/symbols');
|
||||||
|
}
|
||||||
|
|
||||||
|
getLastFundingRate(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('public/linear/funding/prev-funding-rate', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMarkPriceKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('public/linear/mark-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexPriceKline(
|
||||||
|
params: SymbolIntervalFromLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('public/linear/index-price-kline', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPremiumIndexKline(
|
getPremiumIndexKline(
|
||||||
params: SymbolIntervalFromLimitParam
|
params: SymbolIntervalFromLimitParam
|
||||||
): GenericAPIResponse {
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
return this.requestWrapper.get('public/linear/premium-index-kline', params);
|
return this.get('public/linear/premium-index-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data : Advanced
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getOpenInterest(
|
||||||
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/open-interest', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestBigDeal(
|
||||||
|
params: SymbolLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/big-deal', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLongShortRatio(
|
||||||
|
params: SymbolPeriodLimitParam
|
||||||
|
): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('v2/public/account-ratio', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getApiKeyInfo(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/account/api-key');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Wallet Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getWalletBalance(
|
||||||
|
params?: Partial<CoinParam>
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/balance', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWalletFundRecords(
|
||||||
|
params?: WalletFundRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/fund/records', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWithdrawRecords(
|
||||||
|
params?: WithdrawRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/wallet/withdraw/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetExchangeRecords(
|
||||||
|
params?: AssetExchangeRecordsReq
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('v2/private/exchange-order/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime<{}>> {
|
||||||
|
return this.get('v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiAnnouncements(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.get('v2/public/announcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,8 +207,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
close_on_trigger: boolean;
|
close_on_trigger: boolean;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
position_idx?: number;
|
position_idx?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/order/create', params);
|
return this.postPrivate('private/linear/order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveOrderList(params: {
|
getActiveOrderList(params: {
|
||||||
@@ -115,20 +219,22 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
order_status?: string;
|
order_status?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('private/linear/order/list', params);
|
return this.getPrivate('private/linear/order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelActiveOrder(params: {
|
cancelActiveOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/order/cancel', params);
|
return this.postPrivate('private/linear/order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllActiveOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllActiveOrders(
|
||||||
return this.requestWrapper.post('private/linear/order/cancel-all', params);
|
params: SymbolParam
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('private/linear/order/cancel-all', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceActiveOrder(params: {
|
replaceActiveOrder(params: {
|
||||||
@@ -141,16 +247,16 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
stop_loss?: number;
|
stop_loss?: number;
|
||||||
tp_trigger_by?: string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/order/replace', params);
|
return this.postPrivate('private/linear/order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryActiveOrder(params: {
|
queryActiveOrder(params: {
|
||||||
order_id?: string;
|
order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('private/linear/order/search', params);
|
return this.getPrivate('private/linear/order/search', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,8 +280,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
stop_loss?: number;
|
stop_loss?: number;
|
||||||
tp_trigger_by?: string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/stop-order/create', params);
|
return this.postPrivate('private/linear/stop-order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConditionalOrder(params: {
|
getConditionalOrder(params: {
|
||||||
@@ -186,23 +292,22 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
order?: string;
|
order?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('private/linear/stop-order/list', params);
|
return this.getPrivate('private/linear/stop-order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelConditionalOrder(params: {
|
cancelConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/stop-order/cancel', params);
|
return this.postPrivate('private/linear/stop-order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelAllConditionalOrders(params: SymbolParam): GenericAPIResponse {
|
cancelAllConditionalOrders(
|
||||||
return this.requestWrapper.post(
|
params: SymbolParam
|
||||||
'private/linear/stop-order/cancel-all',
|
): Promise<APIResponseWithTime<any>> {
|
||||||
params
|
return this.postPrivate('private/linear/stop-order/cancel-all', params);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceConditionalOrder(params: {
|
replaceConditionalOrder(params: {
|
||||||
@@ -216,35 +321,34 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
stop_loss?: number;
|
stop_loss?: number;
|
||||||
tp_trigger_by?: string;
|
tp_trigger_by?: string;
|
||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/stop-order/replace', params);
|
||||||
'private/linear/stop-order/replace',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queryConditionalOrder(params: {
|
queryConditionalOrder(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
stop_order_id?: string;
|
stop_order_id?: string;
|
||||||
order_link_id?: string;
|
order_link_id?: string;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('private/linear/stop-order/search', params);
|
return this.getPrivate('private/linear/stop-order/search', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position
|
* Position
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getPosition(params?: Partial<SymbolParam>): GenericAPIResponse {
|
getPosition(
|
||||||
return this.requestWrapper.get('private/linear/position/list', params);
|
params?: Partial<SymbolParam>
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('private/linear/position/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAutoAddMargin(params?: {
|
setAutoAddMargin(params?: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
side: string;
|
side: string;
|
||||||
auto_add_margin: boolean;
|
auto_add_margin: boolean;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate(
|
||||||
'private/linear/position/set-auto-add-margin',
|
'private/linear/position/set-auto-add-margin',
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@@ -255,11 +359,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
is_isolated: boolean;
|
is_isolated: boolean;
|
||||||
buy_leverage: number;
|
buy_leverage: number;
|
||||||
sell_leverage: number;
|
sell_leverage: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/position/switch-isolated', params);
|
||||||
'private/linear/position/switch-isolated',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,19 +369,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
setPositionMode(params: {
|
setPositionMode(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
mode: typeof linearPositionModeEnum[keyof typeof linearPositionModeEnum];
|
mode: typeof linearPositionModeEnum[keyof typeof linearPositionModeEnum];
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/position/switch-mode', params);
|
||||||
'private/linear/position/switch-mode',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated use setPositionTpSlMode() instead */
|
|
||||||
setSwitchMode(params?: {
|
|
||||||
symbol: string;
|
|
||||||
tp_sl_mode: typeof positionTpSlModeEnum[keyof typeof positionTpSlModeEnum];
|
|
||||||
}): GenericAPIResponse {
|
|
||||||
return this.requestWrapper.post('private/linear/tpsl/switch-mode', params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,30 +380,24 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
setPositionTpSlMode(params: {
|
setPositionTpSlMode(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
tp_sl_mode: typeof positionTpSlModeEnum[keyof typeof positionTpSlModeEnum];
|
tp_sl_mode: typeof positionTpSlModeEnum[keyof typeof positionTpSlModeEnum];
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post('private/linear/tpsl/switch-mode', params);
|
return this.postPrivate('private/linear/tpsl/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAddReduceMargin(params?: {
|
setAddReduceMargin(params?: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
side: string;
|
side: string;
|
||||||
margin: number;
|
margin: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/position/add-margin', params);
|
||||||
'private/linear/position/add-margin',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLeverage(params: {
|
setUserLeverage(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
buy_leverage: number;
|
buy_leverage: number;
|
||||||
sell_leverage: number;
|
sell_leverage: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/position/set-leverage', params);
|
||||||
'private/linear/position/set-leverage',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradingStop(params: {
|
setTradingStop(params: {
|
||||||
@@ -326,11 +410,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
sl_trigger_by?: string;
|
sl_trigger_by?: string;
|
||||||
sl_size?: number;
|
sl_size?: number;
|
||||||
tp_size?: number;
|
tp_size?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.post(
|
return this.postPrivate('private/linear/position/trading-stop', params);
|
||||||
'private/linear/position/trading-stop',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTradeRecords(params: {
|
getTradeRecords(params: {
|
||||||
@@ -340,11 +421,8 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
exec_type?: string;
|
exec_type?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get(
|
return this.getPrivate('private/linear/trade/execution/list', params);
|
||||||
'private/linear/trade/execution/list',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosedPnl(params: {
|
getClosedPnl(params: {
|
||||||
@@ -354,44 +432,37 @@ export class LinearClient extends SharedEndpoints {
|
|||||||
exec_type?: string;
|
exec_type?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get(
|
return this.getPrivate('private/linear/trade/closed-pnl/list', params);
|
||||||
'private/linear/trade/closed-pnl/list',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Risk Limit
|
* Risk Limit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getRiskLimitList(params: SymbolParam): GenericAPIResponse {
|
getRiskLimitList(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('public/linear/risk-limit', params);
|
return this.getPrivate('public/linear/risk-limit', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRiskLimit(params: {
|
setRiskLimit(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
side: string;
|
side: string;
|
||||||
risk_id: string;
|
risk_id: number;
|
||||||
}): GenericAPIResponse {
|
}): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get('private/linear/position/set-risk', params);
|
return this.postPrivate('private/linear/position/set-risk', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Funding
|
* Funding
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getPredictedFundingFee(params: SymbolParam): GenericAPIResponse {
|
getPredictedFundingFee(
|
||||||
return this.requestWrapper.get(
|
params: SymbolParam
|
||||||
'private/linear/funding/predicted-funding',
|
): Promise<APIResponseWithTime<any>> {
|
||||||
params
|
return this.getPrivate('private/linear/funding/predicted-funding', params);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastFundingFee(params: SymbolParam): GenericAPIResponse {
|
getLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
|
||||||
return this.requestWrapper.get(
|
return this.getPrivate('private/linear/funding/prev-funding', params);
|
||||||
'private/linear/funding/prev-funding',
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ export const DefaultLogger = {
|
|||||||
},
|
},
|
||||||
error: (...params: LogParams): void => {
|
error: (...params: LogParams): void => {
|
||||||
console.error(params);
|
console.error(params);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
@@ -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,13 +1,21 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { KlineInterval } from './types/shared';
|
import { APIResponse, KlineInterval } from './types/shared';
|
||||||
import { NewSpotOrder, OrderSide, OrderTypeSpot, SpotOrderQueryById } from './types/spot';
|
import {
|
||||||
|
NewSpotOrder,
|
||||||
|
OrderSide,
|
||||||
|
OrderTypeSpot,
|
||||||
|
SpotOrderQueryById,
|
||||||
|
SpotSymbolInfo,
|
||||||
|
} from './types/spot';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
import {
|
||||||
import RequestWrapper from './util/requestWrapper';
|
agentSource,
|
||||||
|
getRestBaseUrl,
|
||||||
|
RestClientOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM,
|
||||||
|
} from './util/requestUtils';
|
||||||
|
|
||||||
export class SpotClient extends BaseRestClient {
|
export class SpotClient extends BaseRestClient {
|
||||||
protected requestWrapper: RequestWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the Spot REST API client.
|
* @public Creates an instance of the Spot REST API client.
|
||||||
*
|
*
|
||||||
@@ -24,21 +32,25 @@ export class SpotClient extends BaseRestClient {
|
|||||||
restClientOptions: RestClientOptions = {},
|
restClientOptions: RestClientOptions = {},
|
||||||
requestOptions: AxiosRequestConfig = {}
|
requestOptions: AxiosRequestConfig = {}
|
||||||
) {
|
) {
|
||||||
super(key, secret, getRestBaseUrl(useLivenet, restClientOptions), restClientOptions, requestOptions);
|
super(
|
||||||
|
key,
|
||||||
|
secret,
|
||||||
|
getRestBaseUrl(useLivenet, restClientOptions),
|
||||||
|
restClientOptions,
|
||||||
|
requestOptions,
|
||||||
|
REST_CLIENT_TYPE_ENUM.spot
|
||||||
|
);
|
||||||
|
|
||||||
// this.requestWrapper = new RequestWrapper(
|
|
||||||
// key,
|
|
||||||
// secret,
|
|
||||||
// getRestBaseUrl(useLivenet, restClientOptions),
|
|
||||||
// restClientOptions,
|
|
||||||
// requestOptions
|
|
||||||
// );
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getServerTime(urlKeyOverride?: string): Promise<number> {
|
fetchServerTime(): Promise<number> {
|
||||||
const result = await this.get('/spot/v1/time');
|
return this.getServerTime();
|
||||||
return result.serverTime;
|
}
|
||||||
|
|
||||||
|
async getServerTime(): Promise<number> {
|
||||||
|
const res = await this.get('/spot/v1/time');
|
||||||
|
return res.result.serverTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,17 +59,22 @@ export class SpotClient extends BaseRestClient {
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
|
|
||||||
getSymbols() {
|
getSymbols(): Promise<APIResponse<SpotSymbolInfo[]>> {
|
||||||
return this.get('/spot/v1/symbols');
|
return this.get('/spot/v1/symbols');
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrderBook(symbol: string, limit?: number) {
|
getOrderBook(symbol: string, limit?: number): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/depth', {
|
return this.get('/spot/quote/v1/depth', {
|
||||||
symbol, limit
|
symbol,
|
||||||
|
limit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMergedOrderBook(symbol: string, scale?: number, limit?: number) {
|
getMergedOrderBook(
|
||||||
|
symbol: string,
|
||||||
|
scale?: number,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/depth/merged', {
|
return this.get('/spot/quote/v1/depth/merged', {
|
||||||
symbol,
|
symbol,
|
||||||
scale,
|
scale,
|
||||||
@@ -65,14 +82,20 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrades(symbol: string, limit?: number) {
|
getTrades(symbol: string, limit?: number): Promise<APIResponse<any[]>> {
|
||||||
return this.get('/spot/v1/trades', {
|
return this.get('/spot/quote/v1/trades', {
|
||||||
symbol,
|
symbol,
|
||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCandles(symbol: string, interval: KlineInterval, limit?: number, startTime?: number, endTime?: number) {
|
getCandles(
|
||||||
|
symbol: string,
|
||||||
|
interval: KlineInterval,
|
||||||
|
limit?: number,
|
||||||
|
startTime?: number,
|
||||||
|
endTime?: number
|
||||||
|
): Promise<APIResponse<any[]>> {
|
||||||
return this.get('/spot/quote/v1/kline', {
|
return this.get('/spot/quote/v1/kline', {
|
||||||
symbol,
|
symbol,
|
||||||
interval,
|
interval,
|
||||||
@@ -82,15 +105,15 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get24hrTicker(symbol?: string) {
|
get24hrTicker(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
|
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastTradedPrice(symbol?: string) {
|
getLastTradedPrice(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/price', { symbol });
|
return this.get('/spot/quote/v1/ticker/price', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
getBestBidAskPrice(symbol?: string) {
|
getBestBidAskPrice(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
|
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,31 +121,40 @@ export class SpotClient extends BaseRestClient {
|
|||||||
* Account Data Endpoints
|
* Account Data Endpoints
|
||||||
*/
|
*/
|
||||||
|
|
||||||
submitOrder(params: NewSpotOrder) {
|
submitOrder(params: NewSpotOrder): Promise<APIResponse<any>> {
|
||||||
return this.postPrivate('/spot/v1/order', params);
|
return this.postPrivate('/spot/v1/order', {
|
||||||
|
...params,
|
||||||
|
agentSource,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrder(params: SpotOrderQueryById) {
|
getOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
|
||||||
return this.getPrivate('/spot/v1/order', params);
|
return this.getPrivate('/spot/v1/order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelOrder(params: SpotOrderQueryById) {
|
cancelOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
|
||||||
return this.deletePrivate('/spot/v1/order', params);
|
return this.deletePrivate('/spot/v1/order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelOrderBatch(params: {
|
cancelOrderBatch(params: {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
side?: OrderSide;
|
side?: OrderSide;
|
||||||
orderTypes: OrderTypeSpot[]
|
orderTypes: OrderTypeSpot[];
|
||||||
}) {
|
}): Promise<APIResponse<any>> {
|
||||||
const orderTypes = params.orderTypes ? params.orderTypes.join(',') : undefined;
|
const orderTypes = params.orderTypes
|
||||||
|
? params.orderTypes.join(',')
|
||||||
|
: undefined;
|
||||||
return this.deletePrivate('/spot/order/batch-cancel', {
|
return this.deletePrivate('/spot/order/batch-cancel', {
|
||||||
...params,
|
...params,
|
||||||
orderTypes,
|
orderTypes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getOpenOrders(symbol?: string, orderId?: string, limit?: number) {
|
getOpenOrders(
|
||||||
|
symbol?: string,
|
||||||
|
orderId?: string,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
return this.getPrivate('/spot/v1/open-orders', {
|
return this.getPrivate('/spot/v1/open-orders', {
|
||||||
symbol,
|
symbol,
|
||||||
orderId,
|
orderId,
|
||||||
@@ -130,7 +162,11 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPastOrders(symbol?: string, orderId?: string, limit?: number) {
|
getPastOrders(
|
||||||
|
symbol?: string,
|
||||||
|
orderId?: string,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
return this.getPrivate('/spot/v1/history-orders', {
|
return this.getPrivate('/spot/v1/history-orders', {
|
||||||
symbol,
|
symbol,
|
||||||
orderId,
|
orderId,
|
||||||
@@ -138,7 +174,12 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyTrades(symbol?: string, limit?: number, fromId?: number, toId?: number) {
|
getMyTrades(
|
||||||
|
symbol?: string,
|
||||||
|
limit?: number,
|
||||||
|
fromId?: number,
|
||||||
|
toId?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
return this.getPrivate('/spot/v1/myTrades', {
|
return this.getPrivate('/spot/v1/myTrades', {
|
||||||
symbol,
|
symbol,
|
||||||
limit,
|
limit,
|
||||||
@@ -151,7 +192,7 @@ export class SpotClient extends BaseRestClient {
|
|||||||
* Wallet Data Endpoints
|
* Wallet Data Endpoints
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getBalances() {
|
getBalances(): Promise<APIResponse<any>> {
|
||||||
return this.getPrivate('/spot/v1/account');
|
return this.getPrivate('/spot/v1/account');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export type KlineInterval = '1m'
|
export type KlineInterval =
|
||||||
|
| '1m'
|
||||||
| '3m'
|
| '3m'
|
||||||
| '5m'
|
| '5m'
|
||||||
| '15m'
|
| '15m'
|
||||||
@@ -16,12 +17,17 @@ export type numberInString = string;
|
|||||||
|
|
||||||
export interface APIResponse<T> {
|
export interface APIResponse<T> {
|
||||||
ret_code: number;
|
ret_code: number;
|
||||||
ret_msg: "OK" | string;
|
ret_msg: 'OK' | string;
|
||||||
ext_code: string;
|
ext_code: string | null;
|
||||||
ext_info: string;
|
ext_info: string | null;
|
||||||
result: T;
|
result: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
||||||
|
/** UTC timestamp */
|
||||||
|
time_now: numberInString;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request Parameter Types
|
* Request Parameter Types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { numberInString } from './shared';
|
||||||
|
|
||||||
export type OrderSide = 'Buy' | 'Sell';
|
export type OrderSide = 'Buy' | 'Sell';
|
||||||
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
||||||
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
||||||
@@ -16,3 +18,18 @@ export interface SpotOrderQueryById {
|
|||||||
orderId?: string;
|
orderId?: string;
|
||||||
orderLinkId?: string;
|
orderLinkId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SpotSymbolInfo {
|
||||||
|
name: string;
|
||||||
|
alias: string;
|
||||||
|
baseCurrency: string;
|
||||||
|
quoteCurrency: string;
|
||||||
|
basePrecision: numberInString;
|
||||||
|
quotePrecision: numberInString;
|
||||||
|
minTradeQuantity: numberInString;
|
||||||
|
minTradeAmount: numberInString;
|
||||||
|
minPricePrecision: numberInString;
|
||||||
|
maxTradeQuantity: numberInString;
|
||||||
|
maxTradeAmount: numberInString;
|
||||||
|
category: numberInString;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,37 @@
|
|||||||
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Method } from 'axios';
|
import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios';
|
||||||
|
|
||||||
import { signMessage } from './node-support';
|
import { signMessage } from './node-support';
|
||||||
import { RestClientOptions, GenericAPIResponse, getRestBaseUrl, serializeParams, isPublicEndpoint } from './requestUtils';
|
import {
|
||||||
|
RestClientOptions,
|
||||||
|
serializeParams,
|
||||||
|
RestClientType,
|
||||||
|
REST_CLIENT_TYPE_ENUM,
|
||||||
|
agentSource,
|
||||||
|
} from './requestUtils';
|
||||||
|
|
||||||
|
// axios.interceptors.request.use((request) => {
|
||||||
|
// console.log(new Date(), 'Starting Request', JSON.stringify(request, null, 2));
|
||||||
|
// return request;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// axios.interceptors.response.use((response) => {
|
||||||
|
// console.log(new Date(), 'Response:', JSON.stringify(response, null, 2));
|
||||||
|
// return response;
|
||||||
|
// });
|
||||||
|
|
||||||
|
interface SignedRequestContext {
|
||||||
|
timestamp: number;
|
||||||
|
api_key?: string;
|
||||||
|
recv_window?: number;
|
||||||
|
// spot is diff from the rest...
|
||||||
|
recvWindow?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SignedRequest<T> {
|
||||||
|
originalParams: T & SignedRequestContext;
|
||||||
|
paramsWithSign?: T & SignedRequestContext & { sign: string };
|
||||||
|
sign: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default abstract class BaseRestClient {
|
export default abstract class BaseRestClient {
|
||||||
private timeOffset: number | null;
|
private timeOffset: number | null;
|
||||||
@@ -11,24 +41,31 @@ export default abstract class BaseRestClient {
|
|||||||
private globalRequestOptions: AxiosRequestConfig;
|
private globalRequestOptions: AxiosRequestConfig;
|
||||||
private key: string | undefined;
|
private key: string | undefined;
|
||||||
private secret: string | undefined;
|
private secret: string | undefined;
|
||||||
|
private clientType: RestClientType;
|
||||||
|
|
||||||
|
/** Function that calls exchange API to query & resolve server time, used by time sync */
|
||||||
|
abstract fetchServerTime(): Promise<number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
key: string | undefined,
|
key: string | undefined,
|
||||||
secret: string | undefined,
|
secret: string | undefined,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
options: RestClientOptions = {},
|
options: RestClientOptions = {},
|
||||||
requestOptions: AxiosRequestConfig = {}
|
requestOptions: AxiosRequestConfig = {},
|
||||||
|
clientType: RestClientType
|
||||||
) {
|
) {
|
||||||
this.timeOffset = null;
|
this.timeOffset = null;
|
||||||
this.syncTimePromise = null;
|
this.syncTimePromise = null;
|
||||||
|
|
||||||
|
this.clientType = clientType;
|
||||||
|
|
||||||
this.options = {
|
this.options = {
|
||||||
recv_window: 5000,
|
recv_window: 5000,
|
||||||
// how often to sync time drift with bybit servers
|
// how often to sync time drift with bybit servers
|
||||||
sync_interval_ms: 3600000,
|
sync_interval_ms: 3600000,
|
||||||
// if true, we'll throw errors if any params are undefined
|
// if true, we'll throw errors if any params are undefined
|
||||||
strict_param_validation: false,
|
strict_param_validation: false,
|
||||||
...options
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.globalRequestOptions = {
|
this.globalRequestOptions = {
|
||||||
@@ -37,14 +74,16 @@ export default abstract class BaseRestClient {
|
|||||||
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
||||||
...requestOptions,
|
...requestOptions,
|
||||||
headers: {
|
headers: {
|
||||||
'x-referer': 'bybitapinode'
|
'x-referer': 'bybitapinode',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
|
|
||||||
if (key && !secret) {
|
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) {
|
if (this.options.disable_time_sync !== true) {
|
||||||
@@ -56,31 +95,38 @@ export default abstract class BaseRestClient {
|
|||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(endpoint: string, params?: any): GenericAPIResponse {
|
private isSpotClient() {
|
||||||
|
return this.clientType === REST_CLIENT_TYPE_ENUM.spot;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(endpoint: string, params?: any) {
|
||||||
return this._call('GET', endpoint, params, true);
|
return this._call('GET', endpoint, params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
post(endpoint: string, params?: any): GenericAPIResponse {
|
post(endpoint: string, params?: any) {
|
||||||
return this._call('POST', endpoint, params, true);
|
return this._call('POST', endpoint, params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPrivate(endpoint: string, params?: any): GenericAPIResponse {
|
getPrivate(endpoint: string, params?: any) {
|
||||||
return this._call('GET', endpoint, params, false);
|
return this._call('GET', endpoint, params, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postPrivate(endpoint: string, params?: any): GenericAPIResponse {
|
postPrivate(endpoint: string, params?: any) {
|
||||||
return this._call('POST', endpoint, params, false);
|
return this._call('POST', endpoint, params, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePrivate(endpoint: string, params?: any): GenericAPIResponse {
|
deletePrivate(endpoint: string, params?: any) {
|
||||||
return this._call('DELETE', endpoint, params, false);
|
return this._call('DELETE', endpoint, params, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private async prepareSignParams(params?: any, isPublicApi?: boolean) {
|
||||||
* @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed.
|
if (isPublicApi) {
|
||||||
*/
|
return {
|
||||||
private async _call(method: Method, endpoint: string, params?: any, isPublicApi?: boolean): GenericAPIResponse {
|
originalParams: params,
|
||||||
if (!isPublicApi) {
|
paramsWithSign: params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.key || !this.secret) {
|
if (!this.key || !this.secret) {
|
||||||
throw new Error('Private endpoints require api and private keys set');
|
throw new Error('Private endpoints require api and private keys set');
|
||||||
}
|
}
|
||||||
@@ -89,29 +135,53 @@ export default abstract class BaseRestClient {
|
|||||||
await this.syncTime();
|
await this.syncTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
params = await this.signRequest(params);
|
return this.signRequest(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
): Promise<any> {
|
||||||
const options = {
|
const options = {
|
||||||
...this.globalRequestOptions,
|
...this.globalRequestOptions,
|
||||||
url: [this.baseUrl, endpoint].join(endpoint.startsWith('/') ? '' : '/'),
|
url: [this.baseUrl, endpoint].join(endpoint.startsWith('/') ? '' : '/'),
|
||||||
method: method,
|
method: method,
|
||||||
json: true
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (method === 'GET') {
|
for (const key in params) {
|
||||||
options.params = params;
|
if (typeof params[key] === 'undefined') {
|
||||||
} else {
|
delete params[key];
|
||||||
options.data = params;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return axios(options).then(response => {
|
const signResult = await this.prepareSignParams(params, isPublicApi);
|
||||||
|
|
||||||
|
if (method === 'GET' || this.isSpotClient()) {
|
||||||
|
options.params = signResult.paramsWithSign;
|
||||||
|
if (options.params?.agentSource) {
|
||||||
|
options.data = {
|
||||||
|
agentSource: agentSource,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
options.data = signResult.paramsWithSign;
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios(options)
|
||||||
|
.then((response) => {
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw response;
|
throw response;
|
||||||
}).catch(e => this.parseException(e));
|
})
|
||||||
|
.catch((e) => this.parseException(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,37 +210,53 @@ export default abstract class BaseRestClient {
|
|||||||
message: response.statusText,
|
message: response.statusText,
|
||||||
body: response.data,
|
body: response.data,
|
||||||
headers: response.headers,
|
headers: response.headers,
|
||||||
requestOptions: this.options
|
requestOptions: this.options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private sign request and set recv window
|
* @private sign request and set recv window
|
||||||
*/
|
*/
|
||||||
async signRequest(data: any): Promise<any> {
|
private async signRequest<T extends Object>(
|
||||||
const params = {
|
data: T & SignedRequestContext
|
||||||
|
): Promise<SignedRequest<T>> {
|
||||||
|
const res: SignedRequest<T> = {
|
||||||
|
originalParams: {
|
||||||
...data,
|
...data,
|
||||||
api_key: this.key,
|
api_key: this.key,
|
||||||
timestamp: Date.now() + (this.timeOffset || 0)
|
timestamp: Date.now() + (this.timeOffset || 0),
|
||||||
|
},
|
||||||
|
sign: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
||||||
if (this.options.recv_window && !params.recv_window) {
|
if (this.options.recv_window && !res.originalParams.recv_window) {
|
||||||
params.recv_window = this.options.recv_window;
|
if (this.isSpotClient()) {
|
||||||
|
res.originalParams.recvWindow = this.options.recv_window;
|
||||||
|
} else {
|
||||||
|
res.originalParams.recv_window = this.options.recv_window;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.key && this.secret) {
|
if (this.key && this.secret) {
|
||||||
const serializedParams = serializeParams(params, this.options.strict_param_validation);
|
const serializedParams = serializeParams(
|
||||||
params.sign = await signMessage(serializedParams, this.secret);
|
res.originalParams,
|
||||||
|
this.options.strict_param_validation
|
||||||
|
);
|
||||||
|
res.sign = await signMessage(serializedParams, this.secret);
|
||||||
|
res.paramsWithSign = {
|
||||||
|
...res.originalParams,
|
||||||
|
sign: res.sign,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger time sync and store promise
|
* Trigger time sync and store promise
|
||||||
*/
|
*/
|
||||||
private syncTime(): GenericAPIResponse {
|
private syncTime(): Promise<any> {
|
||||||
if (this.options.disable_time_sync === true) {
|
if (this.options.disable_time_sync === true) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
@@ -179,7 +265,7 @@ export default abstract class BaseRestClient {
|
|||||||
return this.syncTimePromise;
|
return this.syncTimePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncTimePromise = this.fetchTimeOffset().then(offset => {
|
this.syncTimePromise = this.fetchTimeOffset().then((offset) => {
|
||||||
this.timeOffset = offset;
|
this.timeOffset = offset;
|
||||||
this.syncTimePromise = null;
|
this.syncTimePromise = null;
|
||||||
});
|
});
|
||||||
@@ -187,22 +273,27 @@ export default abstract class BaseRestClient {
|
|||||||
return this.syncTimePromise;
|
return this.syncTimePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getServerTime(baseUrlKeyOverride?: string): Promise<number>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate drift based on client<->server latency
|
* Estimate drift based on client<->server latency
|
||||||
*/
|
*/
|
||||||
async fetchTimeOffset(): Promise<number> {
|
async fetchTimeOffset(): Promise<number> {
|
||||||
try {
|
try {
|
||||||
const start = Date.now();
|
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 end = Date.now();
|
||||||
|
|
||||||
const avgDrift = ((end - start) / 2);
|
const avgDrift = (end - start) / 2;
|
||||||
return Math.ceil(serverTime - end + avgDrift);
|
return Math.ceil(serverTime - end + avgDrift);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to fetch get time offset: ', e);
|
console.error('Failed to fetch get time offset: ', e);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ type WsTopicList = Set<WsTopic>;
|
|||||||
interface WsStoredState {
|
interface WsStoredState {
|
||||||
ws?: WebSocket;
|
ws?: WebSocket;
|
||||||
connectionState?: WsConnectionState;
|
connectionState?: WsConnectionState;
|
||||||
activePingTimer?: NodeJS.Timeout | undefined;
|
activePingTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||||
activePongTimer?: NodeJS.Timeout | undefined;
|
activePongTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||||
subscribedTopics: WsTopicList;
|
subscribedTopics: WsTopicList;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class WsStore {
|
export default class WsStore {
|
||||||
private wsState: Record<string, WsStoredState>
|
private wsState: Record<string, WsStoredState>;
|
||||||
private logger: typeof DefaultLogger;
|
private logger: typeof DefaultLogger;
|
||||||
|
|
||||||
constructor(logger: typeof DefaultLogger) {
|
constructor(logger: typeof DefaultLogger) {
|
||||||
@@ -40,11 +39,14 @@ export default class WsStore {
|
|||||||
|
|
||||||
create(key: string): WsStoredState | undefined {
|
create(key: string): WsStoredState | undefined {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
this.logger.warning(
|
||||||
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
|
this.getWs(key)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.wsState[key] = {
|
this.wsState[key] = {
|
||||||
subscribedTopics: new Set(),
|
subscribedTopics: new Set(),
|
||||||
connectionState: WsConnectionState.READY_STATE_INITIAL
|
connectionState: WsConnectionState.READY_STATE_INITIAL,
|
||||||
};
|
};
|
||||||
return this.get(key);
|
return this.get(key);
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,10 @@ export default class WsStore {
|
|||||||
delete(key: string) {
|
delete(key: string) {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
const ws = this.getWs(key);
|
const ws = this.getWs(key);
|
||||||
this.logger.warning('WsStore deleting state for connection still open: ', ws);
|
this.logger.warning(
|
||||||
|
'WsStore deleting state for connection still open: ',
|
||||||
|
ws
|
||||||
|
);
|
||||||
ws?.close();
|
ws?.close();
|
||||||
}
|
}
|
||||||
delete this.wsState[key];
|
delete this.wsState[key];
|
||||||
@@ -70,7 +75,10 @@ export default class WsStore {
|
|||||||
|
|
||||||
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
||||||
if (this.isWsOpen(key)) {
|
if (this.isWsOpen(key)) {
|
||||||
this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
this.logger.warning(
|
||||||
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
|
this.getWs(key)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.get(key, true)!.ws = wsConnection;
|
this.get(key, true)!.ws = wsConnection;
|
||||||
return wsConnection;
|
return wsConnection;
|
||||||
@@ -80,7 +88,10 @@ export default class WsStore {
|
|||||||
|
|
||||||
isWsOpen(key: string): boolean {
|
isWsOpen(key: string): boolean {
|
||||||
const existingConnection = this.getWs(key);
|
const existingConnection = this.getWs(key);
|
||||||
return !!existingConnection && existingConnection.readyState === existingConnection.OPEN;
|
return (
|
||||||
|
!!existingConnection &&
|
||||||
|
existingConnection.readyState === existingConnection.OPEN
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnectionState(key: string): WsConnectionState {
|
getConnectionState(key: string): WsConnectionState {
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
|
export async function signMessage(
|
||||||
export async function signMessage(message: string, secret: string): Promise<string> {
|
message: string,
|
||||||
|
secret: string
|
||||||
|
): Promise<string> {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const key = await window.crypto.subtle.importKey(
|
const key = await window.crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
encoder.encode(secret),
|
encoder.encode(secret),
|
||||||
{name: 'HMAC', hash: {name: 'SHA-256'}},
|
{ name: 'HMAC', hash: { name: 'SHA-256' } },
|
||||||
false,
|
false,
|
||||||
['sign']
|
['sign']
|
||||||
);
|
);
|
||||||
|
|
||||||
const signature = await window.crypto.subtle.sign('HMAC', key, encoder.encode(message));
|
const signature = await window.crypto.subtle.sign(
|
||||||
|
'HMAC',
|
||||||
|
key,
|
||||||
|
encoder.encode(message)
|
||||||
|
);
|
||||||
|
|
||||||
return Array.prototype.map.call(
|
return Array.prototype.map
|
||||||
new Uint8Array(signature),
|
.call(new Uint8Array(signature), (x: any) =>
|
||||||
(x: any) => ('00'+x.toString(16)).slice(-2)
|
('00' + x.toString(16)).slice(-2)
|
||||||
).join('');
|
)
|
||||||
};
|
.join('');
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { createHmac } from 'crypto';
|
import { createHmac } from 'crypto';
|
||||||
|
|
||||||
export async function signMessage(message: string, secret: string): Promise<string> {
|
export async function signMessage(
|
||||||
return createHmac('sha256', secret)
|
message: string,
|
||||||
.update(message)
|
secret: string
|
||||||
.digest('hex');
|
): Promise<string> {
|
||||||
};
|
return createHmac('sha256', secret).update(message).digest('hex');
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,25 +19,31 @@ export interface RestClientOptions {
|
|||||||
parse_exceptions?: boolean;
|
parse_exceptions?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GenericAPIResponse = Promise<any>;
|
export function serializeParams(
|
||||||
|
params: object = {},
|
||||||
export function serializeParams(params: object = {}, strict_validation = false): string {
|
strict_validation = false
|
||||||
|
): string {
|
||||||
return Object.keys(params)
|
return Object.keys(params)
|
||||||
.sort()
|
.sort()
|
||||||
.map(key => {
|
.map((key) => {
|
||||||
const value = params[key];
|
const value = params[key];
|
||||||
if (strict_validation === true && typeof value === 'undefined') {
|
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}`;
|
return `${key}=${value}`;
|
||||||
})
|
})
|
||||||
.join('&');
|
.join('&');
|
||||||
};
|
}
|
||||||
|
|
||||||
export function getRestBaseUrl(useLivenet: boolean, restInverseOptions: RestClientOptions) {
|
export function getRestBaseUrl(
|
||||||
|
useLivenet: boolean,
|
||||||
|
restInverseOptions: RestClientOptions
|
||||||
|
) {
|
||||||
const baseUrlsInverse = {
|
const baseUrlsInverse = {
|
||||||
livenet: 'https://api.bybit.com',
|
livenet: 'https://api.bybit.com',
|
||||||
testnet: 'https://api-testnet.bybit.com'
|
testnet: 'https://api-testnet.bybit.com',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (restInverseOptions.baseUrl) {
|
if (restInverseOptions.baseUrl) {
|
||||||
@@ -50,18 +56,25 @@ export function getRestBaseUrl(useLivenet: boolean, restInverseOptions: RestClie
|
|||||||
return baseUrlsInverse.testnet;
|
return baseUrlsInverse.testnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPublicEndpoint (endpoint: string): boolean {
|
export function isPublicEndpoint(endpoint: string): boolean {
|
||||||
if (endpoint.startsWith('v2/public')) {
|
const publicPrefixes = [
|
||||||
|
'v2/public',
|
||||||
|
'public/linear',
|
||||||
|
'spot/quote/v1',
|
||||||
|
'spot/v1/symbols',
|
||||||
|
'spot/v1/time',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const prefix of publicPrefixes) {
|
||||||
|
if (endpoint.startsWith(prefix)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (endpoint.startsWith('public/linear')) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWsPong(response: any) {
|
export function isWsPong(response: any) {
|
||||||
if (response.pong) {
|
if (response.pong || response.ping) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -71,3 +84,15 @@ export function isWsPong(response: any) {
|
|||||||
response.success === true
|
response.success === true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const agentSource = 'bybitapinode';
|
||||||
|
|
||||||
|
export const REST_CLIENT_TYPE_ENUM = {
|
||||||
|
inverse: 'inverse',
|
||||||
|
inverseFutures: 'inverseFutures',
|
||||||
|
linear: 'linear',
|
||||||
|
spot: 'spot',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type RestClientType =
|
||||||
|
typeof REST_CLIENT_TYPE_ENUM[keyof typeof REST_CLIENT_TYPE_ENUM];
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios';
|
|
||||||
|
|
||||||
import { signMessage } from './node-support';
|
|
||||||
import { serializeParams, RestClientOptions, GenericAPIResponse, isPublicEndpoint } from './requestUtils';
|
|
||||||
|
|
||||||
export default class RequestUtil {
|
|
||||||
private timeOffset: number | null;
|
|
||||||
private syncTimePromise: null | Promise<any>;
|
|
||||||
private options: RestClientOptions;
|
|
||||||
private baseUrl: string;
|
|
||||||
private globalRequestOptions: AxiosRequestConfig;
|
|
||||||
private key: string | undefined;
|
|
||||||
private secret: string | undefined;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
key: string | undefined,
|
|
||||||
secret: string | undefined,
|
|
||||||
baseUrl: string,
|
|
||||||
options: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {}
|
|
||||||
) {
|
|
||||||
this.timeOffset = null;
|
|
||||||
this.syncTimePromise = null;
|
|
||||||
|
|
||||||
this.options = {
|
|
||||||
recv_window: 5000,
|
|
||||||
// how often to sync time drift with bybit servers
|
|
||||||
sync_interval_ms: 3600000,
|
|
||||||
// if true, we'll throw errors if any params are undefined
|
|
||||||
strict_param_validation: false,
|
|
||||||
...options
|
|
||||||
};
|
|
||||||
|
|
||||||
this.globalRequestOptions = {
|
|
||||||
// in ms == 5 minutes by default
|
|
||||||
timeout: 1000 * 60 * 5,
|
|
||||||
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
|
||||||
...requestOptions,
|
|
||||||
headers: {
|
|
||||||
'x-referer': 'bybitapinode'
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
|
|
||||||
if (key && !secret) {
|
|
||||||
throw new Error('API Key & Secret are both required for private enpoints')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.disable_time_sync !== true) {
|
|
||||||
this.syncTime();
|
|
||||||
setInterval(this.syncTime.bind(this), +this.options.sync_interval_ms!);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
this.secret = secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
get<T>(endpoint: string, params?: any): Promise<T> {
|
|
||||||
return this._call('GET', endpoint, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
post<T>(endpoint: string, params?: any): Promise<T> {
|
|
||||||
return this._call('POST', endpoint, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed.
|
|
||||||
*/
|
|
||||||
async _call<T>(method: Method, endpoint: string, params?: any): Promise<T> {
|
|
||||||
if (!isPublicEndpoint(endpoint)) {
|
|
||||||
if (!this.key || !this.secret) {
|
|
||||||
throw new Error('Private endpoints require api and private keys set');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.timeOffset === null) {
|
|
||||||
await this.syncTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
params = await this.signRequest(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
...this.globalRequestOptions,
|
|
||||||
url: [this.baseUrl, endpoint].join('/'),
|
|
||||||
method: method,
|
|
||||||
json: true
|
|
||||||
};
|
|
||||||
|
|
||||||
if (method === 'GET') {
|
|
||||||
options.params = params;
|
|
||||||
} else {
|
|
||||||
options.data = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
return axios(options).then(response => {
|
|
||||||
if (response.status == 200) {
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw response;
|
|
||||||
}).catch(e => this.parseException(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private generic handler to parse request exceptions
|
|
||||||
*/
|
|
||||||
parseException(e: any): unknown {
|
|
||||||
if (this.options.parse_exceptions === false) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something happened in setting up the request that triggered an Error
|
|
||||||
if (!e.response) {
|
|
||||||
if (!e.request) {
|
|
||||||
throw e.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// request made but no response received
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The request was made and the server responded with a status code
|
|
||||||
// that falls out of the range of 2xx
|
|
||||||
const response: AxiosResponse = e.response;
|
|
||||||
throw {
|
|
||||||
code: response.status,
|
|
||||||
message: response.statusText,
|
|
||||||
body: response.data,
|
|
||||||
headers: response.headers,
|
|
||||||
requestOptions: this.options
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private sign request and set recv window
|
|
||||||
*/
|
|
||||||
async signRequest(data: any): Promise<any> {
|
|
||||||
const params = {
|
|
||||||
...data,
|
|
||||||
api_key: this.key,
|
|
||||||
timestamp: Date.now() + (this.timeOffset || 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
|
||||||
if (this.options.recv_window && !params.recv_window) {
|
|
||||||
params.recv_window = this.options.recv_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.key && this.secret) {
|
|
||||||
const serializedParams = serializeParams(params, this.options.strict_param_validation);
|
|
||||||
params.sign = await signMessage(serializedParams, this.secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private trigger time sync and store promise
|
|
||||||
*/
|
|
||||||
syncTime(): GenericAPIResponse {
|
|
||||||
if (this.options.disable_time_sync === true) {
|
|
||||||
return Promise.resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.syncTimePromise !== null) {
|
|
||||||
return this.syncTimePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.syncTimePromise = this.getTimeOffset().then(offset => {
|
|
||||||
this.timeOffset = offset;
|
|
||||||
this.syncTimePromise = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.syncTimePromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated move this somewhere else, because v2/public/time shouldn't be hardcoded here
|
|
||||||
*
|
|
||||||
* @returns {Promise<number>}
|
|
||||||
* @memberof RequestUtil
|
|
||||||
*/
|
|
||||||
async getTimeOffset(): Promise<number> {
|
|
||||||
const start = Date.now();
|
|
||||||
const result = await this.get<any>('v2/public/time');
|
|
||||||
const end = Date.now();
|
|
||||||
|
|
||||||
return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -12,20 +12,20 @@ import WsStore from './util/WsStore';
|
|||||||
|
|
||||||
const inverseEndpoints = {
|
const inverseEndpoints = {
|
||||||
livenet: 'wss://stream.bybit.com/realtime',
|
livenet: 'wss://stream.bybit.com/realtime',
|
||||||
testnet: 'wss://stream-testnet.bybit.com/realtime'
|
testnet: 'wss://stream-testnet.bybit.com/realtime',
|
||||||
};
|
};
|
||||||
|
|
||||||
const linearEndpoints = {
|
const linearEndpoints = {
|
||||||
private: {
|
private: {
|
||||||
livenet: 'wss://stream.bybit.com/realtime_private',
|
livenet: 'wss://stream.bybit.com/realtime_private',
|
||||||
livenet2: 'wss://stream.bytick.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: {
|
public: {
|
||||||
livenet: 'wss://stream.bybit.com/realtime_public',
|
livenet: 'wss://stream.bybit.com/realtime_public',
|
||||||
livenet2: 'wss://stream.bytick.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 = {
|
const spotEndpoints = {
|
||||||
@@ -38,8 +38,8 @@ const spotEndpoints = {
|
|||||||
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
|
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
|
||||||
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
|
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
|
||||||
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
|
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
const loggerCategory = { category: 'bybit-ws' };
|
const loggerCategory = { category: 'bybit-ws' };
|
||||||
|
|
||||||
@@ -54,62 +54,71 @@ export enum WsConnectionState {
|
|||||||
READY_STATE_CONNECTING,
|
READY_STATE_CONNECTING,
|
||||||
READY_STATE_CONNECTED,
|
READY_STATE_CONNECTED,
|
||||||
READY_STATE_CLOSING,
|
READY_STATE_CLOSING,
|
||||||
READY_STATE_RECONNECTING
|
READY_STATE_RECONNECTING,
|
||||||
};
|
}
|
||||||
|
|
||||||
export type APIMarket = 'inverse' | 'linear' | 'spot';
|
export type APIMarket = 'inverse' | 'linear' | 'spot';
|
||||||
|
|
||||||
// Same as inverse futures
|
// Same as inverse futures
|
||||||
export type WsPublicInverseTopic = 'orderBookL2_25'
|
export type WsPublicInverseTopic =
|
||||||
|
| 'orderBookL2_25'
|
||||||
| 'orderBookL2_200'
|
| 'orderBookL2_200'
|
||||||
| 'trade'
|
| 'trade'
|
||||||
| 'insurance'
|
| 'insurance'
|
||||||
| 'instrument_info'
|
| 'instrument_info'
|
||||||
| 'klineV2';
|
| 'klineV2';
|
||||||
|
|
||||||
export type WsPublicUSDTPerpTopic = 'orderBookL2_25'
|
export type WsPublicUSDTPerpTopic =
|
||||||
|
| 'orderBookL2_25'
|
||||||
| 'orderBookL2_200'
|
| 'orderBookL2_200'
|
||||||
| 'trade'
|
| 'trade'
|
||||||
| 'insurance'
|
| 'insurance'
|
||||||
| 'instrument_info'
|
| 'instrument_info'
|
||||||
| 'kline';
|
| 'kline';
|
||||||
|
|
||||||
export type WsPublicSpotV1Topic = 'trade'
|
export type WsPublicSpotV1Topic =
|
||||||
|
| 'trade'
|
||||||
| 'realtimes'
|
| 'realtimes'
|
||||||
| 'kline'
|
| 'kline'
|
||||||
| 'depth'
|
| 'depth'
|
||||||
| 'mergedDepth'
|
| 'mergedDepth'
|
||||||
| 'diffDepth';
|
| 'diffDepth';
|
||||||
|
|
||||||
export type WsPublicSpotV2Topic = 'depth'
|
export type WsPublicSpotV2Topic =
|
||||||
|
| 'depth'
|
||||||
| 'kline'
|
| 'kline'
|
||||||
| 'trade'
|
| 'trade'
|
||||||
| 'bookTicker'
|
| 'bookTicker'
|
||||||
| 'realtimes';
|
| 'realtimes';
|
||||||
|
|
||||||
export type WsPublicTopics = WsPublicInverseTopic
|
export type WsPublicTopics =
|
||||||
|
| WsPublicInverseTopic
|
||||||
| WsPublicUSDTPerpTopic
|
| WsPublicUSDTPerpTopic
|
||||||
| WsPublicSpotV1Topic
|
| WsPublicSpotV1Topic
|
||||||
| WsPublicSpotV2Topic
|
| WsPublicSpotV2Topic
|
||||||
| string;
|
| string;
|
||||||
|
|
||||||
// Same as inverse futures
|
// Same as inverse futures
|
||||||
export type WsPrivateInverseTopic = 'position'
|
export type WsPrivateInverseTopic =
|
||||||
|
| 'position'
|
||||||
| 'execution'
|
| 'execution'
|
||||||
| 'order'
|
| 'order'
|
||||||
| 'stop_order';
|
| 'stop_order';
|
||||||
|
|
||||||
export type WsPrivateUSDTPerpTopic = 'position'
|
export type WsPrivateUSDTPerpTopic =
|
||||||
|
| 'position'
|
||||||
| 'execution'
|
| 'execution'
|
||||||
| 'order'
|
| 'order'
|
||||||
| 'stop_order'
|
| 'stop_order'
|
||||||
| 'wallet';
|
| 'wallet';
|
||||||
|
|
||||||
export type WsPrivateSpotTopic = 'outboundAccountInfo'
|
export type WsPrivateSpotTopic =
|
||||||
|
| 'outboundAccountInfo'
|
||||||
| 'executionReport'
|
| 'executionReport'
|
||||||
| 'ticketInfo';
|
| 'ticketInfo';
|
||||||
|
|
||||||
export type WsPrivateTopic = WsPrivateInverseTopic
|
export type WsPrivateTopic =
|
||||||
|
| WsPrivateInverseTopic
|
||||||
| WsPrivateUSDTPerpTopic
|
| WsPrivateUSDTPerpTopic
|
||||||
| WsPrivateSpotTopic
|
| WsPrivateSpotTopic
|
||||||
| string;
|
| string;
|
||||||
@@ -135,7 +144,7 @@ export interface WSClientConfigurableOptions {
|
|||||||
restOptions?: any;
|
restOptions?: any;
|
||||||
requestOptions?: any;
|
requestOptions?: any;
|
||||||
wsUrl?: string;
|
wsUrl?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
||||||
livenet: boolean;
|
livenet: boolean;
|
||||||
@@ -147,8 +156,7 @@ export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
|||||||
pongTimeout: number;
|
pongTimeout: number;
|
||||||
pingInterval: number;
|
pingInterval: number;
|
||||||
reconnectTimeout: number;
|
reconnectTimeout: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export const wsKeyInverse = 'inverse';
|
export const wsKeyInverse = 'inverse';
|
||||||
export const wsKeyLinearPrivate = 'linearPrivate';
|
export const wsKeyLinearPrivate = 'linearPrivate';
|
||||||
@@ -157,29 +165,54 @@ export const wsKeySpotPrivate = 'spotPrivate';
|
|||||||
export const wsKeySpotPublic = 'spotPublic';
|
export const wsKeySpotPublic = 'spotPublic';
|
||||||
|
|
||||||
// This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets)
|
// 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 getLinearWsKeyForTopic = (topic: string): WsKey => {
|
||||||
const privateLinearTopics = ['position', 'execution', 'order', 'stop_order', 'wallet'];
|
const privateLinearTopics = [
|
||||||
|
'position',
|
||||||
|
'execution',
|
||||||
|
'order',
|
||||||
|
'stop_order',
|
||||||
|
'wallet',
|
||||||
|
];
|
||||||
if (privateLinearTopics.includes(topic)) {
|
if (privateLinearTopics.includes(topic)) {
|
||||||
return wsKeyLinearPrivate;
|
return wsKeyLinearPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return wsKeyLinearPublic;
|
return wsKeyLinearPublic;
|
||||||
}
|
};
|
||||||
const getSpotWsKeyForTopic = (topic: string): WsKey => {
|
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)) {
|
if (privateLinearTopics.includes(topic)) {
|
||||||
return wsKeySpotPrivate;
|
return wsKeySpotPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return wsKeySpotPublic;
|
return wsKeySpotPublic;
|
||||||
}
|
};
|
||||||
|
|
||||||
export declare interface WebsocketClient {
|
export declare interface WebsocketClient {
|
||||||
on(event: 'open' | 'reconnected', listener: ({ wsKey: WsKey, event: any }) => void): this;
|
on(
|
||||||
on(event: 'response' | 'update' | 'error', listener: (response: any) => void): this;
|
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;
|
on(event: 'reconnect' | 'close', listener: ({ wsKey: WsKey }) => void): this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +229,10 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
private options: WebsocketClientOptions;
|
private options: WebsocketClientOptions;
|
||||||
private wsStore: WsStore;
|
private wsStore: WsStore;
|
||||||
|
|
||||||
constructor(options: WSClientConfigurableOptions, logger?: typeof DefaultLogger) {
|
constructor(
|
||||||
|
options: WSClientConfigurableOptions,
|
||||||
|
logger?: typeof DefaultLogger
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.logger = logger || DefaultLogger;
|
this.logger = logger || DefaultLogger;
|
||||||
@@ -207,7 +243,7 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
pongTimeout: 1000,
|
pongTimeout: 1000,
|
||||||
pingInterval: 10000,
|
pingInterval: 10000,
|
||||||
reconnectTimeout: 500,
|
reconnectTimeout: 500,
|
||||||
...options
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.options.market) {
|
if (!this.options.market) {
|
||||||
@@ -215,13 +251,31 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLinear()) {
|
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()) {
|
} else if (this.isSpot()) {
|
||||||
// TODO: spot client
|
// 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();
|
this.connectPublic();
|
||||||
} else {
|
} 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) {
|
public subscribe(wsTopics: WsTopic[] | WsTopic) {
|
||||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||||
topics.forEach(topic => this.wsStore.addTopic(
|
topics.forEach((topic) =>
|
||||||
this.getWsKeyForTopic(topic),
|
this.wsStore.addTopic(this.getWsKeyForTopic(topic), topic)
|
||||||
topic
|
);
|
||||||
));
|
|
||||||
|
|
||||||
// attempt to send subscription topic per websocket
|
// attempt to send subscription topic per websocket
|
||||||
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
||||||
@@ -273,10 +326,9 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
public unsubscribe(wsTopics: WsTopic[] | WsTopic) {
|
public unsubscribe(wsTopics: WsTopic[] | WsTopic) {
|
||||||
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
|
||||||
topics.forEach(topic => this.wsStore.deleteTopic(
|
topics.forEach((topic) =>
|
||||||
this.getWsKeyForTopic(topic),
|
this.wsStore.deleteTopic(this.getWsKeyForTopic(topic), topic)
|
||||||
topic
|
);
|
||||||
));
|
|
||||||
|
|
||||||
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
this.wsStore.getKeys().forEach((wsKey: WsKey) => {
|
||||||
// unsubscribe request only necessary if active connection exists
|
// unsubscribe request only necessary if active connection exists
|
||||||
@@ -303,7 +355,10 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLinear()) {
|
if (this.isLinear()) {
|
||||||
return [this.connect(wsKeyLinearPublic), this.connect(wsKeyLinearPrivate)];
|
return [
|
||||||
|
this.connect(wsKeyLinearPublic),
|
||||||
|
this.connect(wsKeyLinearPrivate),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isSpot()) {
|
if (this.isSpot()) {
|
||||||
@@ -342,12 +397,18 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
private async connect(wsKey: WsKey): Promise<WebSocket | undefined> {
|
private async connect(wsKey: WsKey): Promise<WebSocket | undefined> {
|
||||||
try {
|
try {
|
||||||
if (this.wsStore.isWsOpen(wsKey)) {
|
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);
|
return this.wsStore.getWs(wsKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,11 +438,17 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
|
|
||||||
switch (error.message) {
|
switch (error.message) {
|
||||||
case 'Unexpected server response: 401':
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,23 +459,39 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
private async getAuthParams(wsKey: WsKey): Promise<string> {
|
private async getAuthParams(wsKey: WsKey): Promise<string> {
|
||||||
const { key, secret } = this.options;
|
const { key, secret } = this.options;
|
||||||
|
|
||||||
if (key && secret && wsKey !== wsKeyLinearPublic && wsKey !== wsKeySpotPublic) {
|
if (
|
||||||
this.logger.debug('Getting auth\'d request params', { ...loggerCategory, wsKey });
|
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 = {
|
const params: any = {
|
||||||
api_key: this.options.key,
|
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);
|
return '?' + serializeParams(params);
|
||||||
|
|
||||||
} else if (!key || !secret) {
|
} 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 {
|
} else {
|
||||||
this.logger.debug('Starting public only websocket client.', { ...loggerCategory, wsKey });
|
this.logger.debug('Starting public only websocket client.', {
|
||||||
|
...loggerCategory,
|
||||||
|
wsKey,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
@@ -421,7 +504,10 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.logger.info('Reconnecting to websocket', { ...loggerCategory, wsKey });
|
this.logger.info('Reconnecting to websocket', {
|
||||||
|
...loggerCategory,
|
||||||
|
wsKey,
|
||||||
|
});
|
||||||
this.connect(wsKey);
|
this.connect(wsKey);
|
||||||
}, connectionDelayMs);
|
}, connectionDelayMs);
|
||||||
}
|
}
|
||||||
@@ -433,7 +519,10 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' }));
|
this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' }));
|
||||||
|
|
||||||
this.wsStore.get(wsKey, true)!.activePongTimer = setTimeout(() => {
|
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.getWs(wsKey)?.close();
|
||||||
}, this.options.pongTimeout);
|
}, this.options.pongTimeout);
|
||||||
}
|
}
|
||||||
@@ -470,7 +559,7 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
const wsMessage = JSON.stringify({
|
const wsMessage = JSON.stringify({
|
||||||
op: 'subscribe',
|
op: 'subscribe',
|
||||||
args: topics
|
args: topics,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tryWsSend(wsKey, wsMessage);
|
this.tryWsSend(wsKey, wsMessage);
|
||||||
@@ -485,7 +574,7 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
const wsMessage = JSON.stringify({
|
const wsMessage = JSON.stringify({
|
||||||
op: 'unsubscribe',
|
op: 'unsubscribe',
|
||||||
args: topics
|
args: topics,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tryWsSend(wsKey, wsMessage);
|
this.tryWsSend(wsKey, wsMessage);
|
||||||
@@ -493,38 +582,62 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
|
|
||||||
private tryWsSend(wsKey: WsKey, wsMessage: string) {
|
private tryWsSend(wsKey: WsKey, wsMessage: string) {
|
||||||
try {
|
try {
|
||||||
this.logger.silly(`Sending upstream ws message: `, { ...loggerCategory, wsMessage, wsKey });
|
this.logger.silly(`Sending upstream ws message: `, {
|
||||||
|
...loggerCategory,
|
||||||
|
wsMessage,
|
||||||
|
wsKey,
|
||||||
|
});
|
||||||
if (!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);
|
const ws = this.getWs(wsKey);
|
||||||
if (!ws) {
|
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);
|
ws.send(wsMessage);
|
||||||
} catch (e) {
|
} 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 {
|
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 agent = this.options.requestOptions?.agent;
|
||||||
const ws = new WebSocket(url, undefined, agent ? { agent } : undefined);
|
const ws = new WebSocket(url, undefined, agent ? { agent } : undefined);
|
||||||
ws.onopen = event => this.onWsOpen(event, wsKey);
|
ws.onopen = (event) => this.onWsOpen(event, wsKey);
|
||||||
ws.onmessage = event => this.onWsMessage(event, wsKey);
|
ws.onmessage = (event) => this.onWsMessage(event, wsKey);
|
||||||
ws.onerror = event => this.onWsError(event, wsKey);
|
ws.onerror = (event) => this.onWsError(event, wsKey);
|
||||||
ws.onclose = event => this.onWsClose(event, wsKey);
|
ws.onclose = (event) => this.onWsClose(event, wsKey);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWsOpen(event, wsKey: WsKey) {
|
private onWsOpen(event, wsKey: WsKey) {
|
||||||
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) {
|
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 });
|
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.logger.info('Websocket reconnected', { ...loggerCategory, wsKey });
|
||||||
this.emit('reconnected', { wsKey, event });
|
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
|
// any message can clear the pong timer - wouldn't get a message if the ws dropped
|
||||||
this.clearPongTimer(wsKey);
|
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) {
|
if ('success' in msg || msg?.pong) {
|
||||||
this.onWsMessageResponse(msg, wsKey);
|
this.onWsMessageResponse(msg, wsKey);
|
||||||
} else if (msg.topic) {
|
} else if (msg.topic) {
|
||||||
this.onWsMessageUpdate(msg);
|
this.onWsMessageUpdate(msg);
|
||||||
} else {
|
} 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) {
|
} 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) {
|
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) {
|
if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) {
|
||||||
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
|
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
|
||||||
@@ -606,7 +732,7 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
|
|
||||||
const networkKey = this.isLivenet() ? 'livenet' : 'testnet';
|
const networkKey = this.isLivenet() ? 'livenet' : 'testnet';
|
||||||
// TODO: reptitive
|
// TODO: reptitive
|
||||||
if (this.isLinear() || wsKey.startsWith('linear')){
|
if (this.isLinear() || wsKey.startsWith('linear')) {
|
||||||
if (wsKey === wsKeyLinearPublic) {
|
if (wsKey === wsKeyLinearPublic) {
|
||||||
return linearEndpoints.public[networkKey];
|
return linearEndpoints.public[networkKey];
|
||||||
}
|
}
|
||||||
@@ -615,11 +741,14 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
return linearEndpoints.private[networkKey];
|
return linearEndpoints.private[networkKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.error('Unhandled linear wsKey: ', { ...loggerCategory, wsKey });
|
this.logger.error('Unhandled linear wsKey: ', {
|
||||||
|
...loggerCategory,
|
||||||
|
wsKey,
|
||||||
|
});
|
||||||
return linearEndpoints[networkKey];
|
return linearEndpoints[networkKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isSpot() || wsKey.startsWith('spot')){
|
if (this.isSpot() || wsKey.startsWith('spot')) {
|
||||||
if (wsKey === wsKeySpotPublic) {
|
if (wsKey === wsKeySpotPublic) {
|
||||||
return spotEndpoints.public[networkKey];
|
return spotEndpoints.public[networkKey];
|
||||||
}
|
}
|
||||||
@@ -641,13 +770,15 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
return wsKeyInverse;
|
return wsKeyInverse;
|
||||||
}
|
}
|
||||||
if (this.isLinear()) {
|
if (this.isLinear()) {
|
||||||
return getLinearWsKeyForTopic(topic)
|
return getLinearWsKeyForTopic(topic);
|
||||||
}
|
}
|
||||||
return getSpotWsKeyForTopic(topic);
|
return getSpotWsKeyForTopic(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private wrongMarketError(market: APIMarket) {
|
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.
|
// TODO: persistance for subbed topics. Look at ftx-api implementation.
|
||||||
@@ -656,14 +787,17 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
throw this.wrongMarketError('spot');
|
throw this.wrongMarketError('spot');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
return this.tryWsSend(
|
||||||
|
wsKeySpotPublic,
|
||||||
|
JSON.stringify({
|
||||||
topic: 'trade',
|
topic: 'trade',
|
||||||
event: 'sub',
|
event: 'sub',
|
||||||
symbol,
|
symbol,
|
||||||
params: {
|
params: {
|
||||||
binary: !!binary,
|
binary: !!binary,
|
||||||
}
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribePublicSpotTradingPair(symbol: string, binary?: boolean) {
|
public subscribePublicSpotTradingPair(symbol: string, binary?: boolean) {
|
||||||
@@ -671,35 +805,50 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
throw this.wrongMarketError('spot');
|
throw this.wrongMarketError('spot');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
return this.tryWsSend(
|
||||||
|
wsKeySpotPublic,
|
||||||
|
JSON.stringify({
|
||||||
symbol,
|
symbol,
|
||||||
topic: 'realtimes',
|
topic: 'realtimes',
|
||||||
event: 'sub',
|
event: 'sub',
|
||||||
params: {
|
params: {
|
||||||
binary: !!binary,
|
binary: !!binary,
|
||||||
},
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribePublicSpotV1Kline(symbol: string, candleSize: KlineInterval, binary?: boolean) {
|
public subscribePublicSpotV1Kline(
|
||||||
|
symbol: string,
|
||||||
|
candleSize: KlineInterval,
|
||||||
|
binary?: boolean
|
||||||
|
) {
|
||||||
if (!this.isSpot()) {
|
if (!this.isSpot()) {
|
||||||
throw this.wrongMarketError('spot');
|
throw this.wrongMarketError('spot');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify({
|
return this.tryWsSend(
|
||||||
|
wsKeySpotPublic,
|
||||||
|
JSON.stringify({
|
||||||
symbol,
|
symbol,
|
||||||
topic: 'kline_' + candleSize,
|
topic: 'kline_' + candleSize,
|
||||||
event: 'sub',
|
event: 'sub',
|
||||||
params: {
|
params: {
|
||||||
binary: !!binary,
|
binary: !!binary,
|
||||||
},
|
},
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ws.send('{"symbol":"BTCUSDT","topic":"depth","event":"sub","params":{"binary":false}}');
|
//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":"mergedDepth","event":"sub","params":{"binary":false,"dumpScale":1}}');
|
||||||
//ws.send('{"symbol":"BTCUSDT","topic":"diffDepth","event":"sub","params":{"binary":false}}');
|
//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()) {
|
if (!this.isSpot()) {
|
||||||
throw this.wrongMarketError('spot');
|
throw this.wrongMarketError('spot');
|
||||||
}
|
}
|
||||||
@@ -709,7 +858,7 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
case 'full': {
|
case 'full': {
|
||||||
topic = 'depth';
|
topic = 'depth';
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
case 'merge': {
|
case 'merge': {
|
||||||
topic = 'mergedDepth';
|
topic = 'mergedDepth';
|
||||||
if (!dumpScale) {
|
if (!dumpScale) {
|
||||||
@@ -736,5 +885,4 @@ export class WebsocketClient extends EventEmitter {
|
|||||||
}
|
}
|
||||||
return this.tryWsSend(wsKeySpotPublic, JSON.stringify(msg));
|
return this.tryWsSend(wsKeySpotPublic, JSON.stringify(msg));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|||||||
105
test/inverse-futures/private.read.test.ts
Normal file
105
test/inverse-futures/private.read.test.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { InverseFuturesClient } from '../../src/inverse-futures-client';
|
||||||
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Inverse-Futures REST API GET Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const api = new InverseFuturesClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
|
const symbol = 'BTCUSDU22';
|
||||||
|
|
||||||
|
it('getApiKeyInfo()', async () => {
|
||||||
|
expect(await api.getApiKeyInfo()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletBalance()', async () => {
|
||||||
|
expect(await api.getWalletBalance()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletFundRecords()', async () => {
|
||||||
|
expect(await api.getWalletFundRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWithdrawRecords()', async () => {
|
||||||
|
expect(await api.getWithdrawRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetExchangeRecords()', async () => {
|
||||||
|
expect(await api.getAssetExchangeRecords()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getActiveOrderList()', async () => {
|
||||||
|
expect(await api.getActiveOrderList({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryActiveOrder()', async () => {
|
||||||
|
expect(await api.queryActiveOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getConditionalOrder()', async () => {
|
||||||
|
expect(await api.getConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryConditionalOrder()', async () => {
|
||||||
|
expect(await api.queryConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPosition()', async () => {
|
||||||
|
expect(await api.getPosition()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTradeRecords()', async () => {
|
||||||
|
expect(await api.getTradeRecords({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getClosedPnl()', async () => {
|
||||||
|
expect(await api.getClosedPnl({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getRiskLimitList()', async () => {
|
||||||
|
expect(await api.getRiskLimitList()).toMatchObject(
|
||||||
|
successResponseList('ok')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMyLastFundingFee()', async () => {
|
||||||
|
expect(await api.getMyLastFundingFee({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPredictedFunding()', async () => {
|
||||||
|
expect(await api.getPredictedFunding({ symbol: 'BTCUSD' })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLcpInfo()', async () => {
|
||||||
|
expect(await api.getLcpInfo({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
198
test/inverse-futures/private.write.test.ts
Normal file
198
test/inverse-futures/private.write.test.ts
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import { API_ERROR_CODE, InverseFuturesClient } from '../../src';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new InverseFuturesClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
|
const symbol = 'BTCUSDU22';
|
||||||
|
|
||||||
|
// These tests are primarily check auth is working by expecting balance or order not found style errors
|
||||||
|
|
||||||
|
it('placeActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeActiveOrder({
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
order_type: 'Limit',
|
||||||
|
price: 30000,
|
||||||
|
qty: 1,
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
|
ret_msg: 'position idx not match position mode',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelActiveOrder({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllActiveOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllActiveOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceActiveOrder({
|
||||||
|
symbol,
|
||||||
|
order_id: '123123123',
|
||||||
|
p_r_qty: '1',
|
||||||
|
p_r_price: '30000',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('placeConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeConditionalOrder({
|
||||||
|
order_type: 'Limit',
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
qty: '1',
|
||||||
|
price: '8100',
|
||||||
|
base_price: '8300',
|
||||||
|
stop_px: '8150',
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
order_link_id: 'cus_order_id_1',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
|
ret_msg: 'position idx not match position mode',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
order_link_id: 'lkasmdflasd',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllConditionalOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllConditionalOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
p_r_price: '50000',
|
||||||
|
p_r_qty: 1,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changePositionMargin()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.changePositionMargin({
|
||||||
|
symbol,
|
||||||
|
margin: '10',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
|
ret_msg: 'position idx not match position mode',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setTradingStop()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setTradingStop({
|
||||||
|
symbol,
|
||||||
|
take_profit: 50000,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_STATUS_NOT_NORMAL,
|
||||||
|
ret_msg: 'position status is not normal',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setUserLeverage()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setUserLeverage({
|
||||||
|
symbol,
|
||||||
|
buy_leverage: 5,
|
||||||
|
sell_leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.LEVERAGE_NOT_MODIFIED,
|
||||||
|
ret_msg: 'leverage not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setPositionMode()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setPositionMode({
|
||||||
|
symbol,
|
||||||
|
mode: 3,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_MODE_NOT_MODIFIED,
|
||||||
|
ret_msg: 'position mode not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setMarginType()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setMarginType({
|
||||||
|
symbol,
|
||||||
|
is_isolated: false,
|
||||||
|
buy_leverage: 5,
|
||||||
|
sell_leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ISOLATED_NOT_MODIFIED,
|
||||||
|
ret_msg: 'Isolated not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setRiskLimit()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setRiskLimit({
|
||||||
|
symbol,
|
||||||
|
risk_id: 'myriskid',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: -1,
|
||||||
|
ret_msg: `Currently not support symbol[${symbol}]`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,66 +1,93 @@
|
|||||||
import { InverseFuturesClient } from "../../src/inverse-futures-client";
|
import { InverseFuturesClient } from '../../src/inverse-futures-client';
|
||||||
import { notAuthenticatedError, successResponseList, successResponseObject } from "../response.util";
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Inverse Futures REST API Endpoints', () => {
|
describe('Public Inverse Futures REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const useLivenet = true;
|
||||||
const api = new InverseFuturesClient(undefined, undefined, useLivenet, { disable_time_sync: true });
|
const api = new InverseFuturesClient(undefined, undefined, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
const timestampOneHourAgo = (new Date().getTime() / 1000) - (1000 * 60 * 60);
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
const from = Number(timestampOneHourAgo.toFixed(0));
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
describe('Inverse-Futures only endpoints', () => {
|
describe('Inverse-Futures only endpoints', () => {
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
expect(() => api.getPosition()).rejects.toMatchObject(notAuthenticatedError());
|
expect(() => api.getPosition()).rejects.toMatchObject(
|
||||||
});
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
it('getKline()', async () => {
|
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(
|
||||||
expect(
|
notAuthenticatedError()
|
||||||
await api.getKline({ symbol, interval, from })
|
);
|
||||||
).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getTrades()', async () => {
|
|
||||||
expect(await api.getTrades({ symbol })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getIndexPriceKline()', async () => {
|
|
||||||
expect(await api.getIndexPriceKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getPremiumIndexKline()', async () => {
|
|
||||||
expect(await api.getPremiumIndexKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getLastFundingRate()', async () => {
|
|
||||||
expect(await api.getLastFundingRate({ symbol })).toMatchObject(successResponseObject());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Shared endpoints', () => {
|
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
|
||||||
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(notAuthenticatedError());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getOrderBook()', async () => {
|
it('getOrderBook()', async () => {
|
||||||
expect(await api.getOrderBook({ symbol })).toMatchObject(successResponseList());
|
expect(await api.getOrderBook({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getKline()', async () => {
|
||||||
|
expect(await api.getKline({ symbol, interval, from })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getTickers()', async () => {
|
it('getTickers()', async () => {
|
||||||
expect(await api.getTickers()).toMatchObject(successResponseList());
|
expect(await api.getTickers()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('getSymbols()', async () => {
|
it('getSymbols()', async () => {
|
||||||
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getMarkPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getMarkPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getIndexPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPremiumIndexKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getPremiumIndexKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingRate()', async () => {
|
||||||
|
expect(await api.getLastFundingRate({ symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('getServerTime()', async () => {
|
it('getServerTime()', async () => {
|
||||||
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fetchServertime() returns number', async () => {
|
||||||
|
expect(await api.fetchServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
it('getApiAnnouncements()', async () => {
|
it('getApiAnnouncements()', async () => {
|
||||||
expect(await api.getApiAnnouncements()).toMatchObject(successResponseList());
|
expect(await api.getApiAnnouncements()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
103
test/inverse/private.read.test.ts
Normal file
103
test/inverse/private.read.test.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { InverseClient } from '../../src/inverse-client';
|
||||||
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Inverse REST API Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new InverseClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSD';
|
||||||
|
|
||||||
|
it('getApiKeyInfo()', async () => {
|
||||||
|
expect(await api.getApiKeyInfo()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletBalance()', async () => {
|
||||||
|
expect(await api.getWalletBalance()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletFundRecords()', async () => {
|
||||||
|
expect(await api.getWalletFundRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWithdrawRecords()', async () => {
|
||||||
|
expect(await api.getWithdrawRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetExchangeRecords()', async () => {
|
||||||
|
expect(await api.getAssetExchangeRecords()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getActiveOrderList()', async () => {
|
||||||
|
expect(await api.getActiveOrderList({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryActiveOrder()', async () => {
|
||||||
|
expect(await api.queryActiveOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getConditionalOrder()', async () => {
|
||||||
|
expect(await api.getConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryConditionalOrder()', async () => {
|
||||||
|
expect(await api.queryConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPosition()', async () => {
|
||||||
|
expect(await api.getPosition()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTradeRecords()', async () => {
|
||||||
|
expect(await api.getTradeRecords({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getRiskLimitList()', async () => {
|
||||||
|
expect(await api.getRiskLimitList()).toMatchObject(
|
||||||
|
successResponseList('ok')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getClosedPnl()', async () => {
|
||||||
|
expect(await api.getClosedPnl({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMyLastFundingFee()', async () => {
|
||||||
|
expect(await api.getMyLastFundingFee({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLcpInfo()', async () => {
|
||||||
|
expect(await api.getLcpInfo({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
193
test/inverse/private.write.test.ts
Normal file
193
test/inverse/private.write.test.ts
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import { API_ERROR_CODE } from '../../src';
|
||||||
|
import { InverseClient } from '../../src/inverse-client';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Inverse REST API Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new InverseClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSD';
|
||||||
|
|
||||||
|
// These tests are primarily check auth is working by expecting balance or order not found style errors
|
||||||
|
|
||||||
|
it('placeActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeActiveOrder({
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
order_type: 'Limit',
|
||||||
|
price: 30000,
|
||||||
|
qty: 1,
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.INSUFFICIENT_BALANCE_FOR_ORDER_COST,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelActiveOrder({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllActiveOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllActiveOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceActiveOrder({
|
||||||
|
symbol,
|
||||||
|
order_id: '123123123',
|
||||||
|
p_r_qty: 1,
|
||||||
|
p_r_price: '30000',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('placeConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeConditionalOrder({
|
||||||
|
order_type: 'Limit',
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
qty: '1',
|
||||||
|
price: '8100',
|
||||||
|
base_price: '8300',
|
||||||
|
stop_px: '8150',
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
order_link_id: 'cus_order_id_1',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.INSUFFICIENT_BALANCE,
|
||||||
|
ret_msg: 'Insufficient wallet balance',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
order_link_id: 'lkasmdflasd',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllConditionalOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllConditionalOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
p_r_price: '50000',
|
||||||
|
p_r_qty: 1,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changePositionMargin()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.changePositionMargin({
|
||||||
|
symbol,
|
||||||
|
margin: '10',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_IS_CROSS_MARGIN,
|
||||||
|
ret_msg: 'position is in crossMargin',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setTradingStop()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setTradingStop({
|
||||||
|
symbol,
|
||||||
|
take_profit: 5555,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.CANNOT_SET_TRADING_STOP_FOR_ZERO_POS,
|
||||||
|
ret_msg: 'can not set tp/sl/ts for zero position',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setUserLeverage()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setUserLeverage({
|
||||||
|
symbol,
|
||||||
|
leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
result: 5,
|
||||||
|
ret_code: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setSlTpPositionMode()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setSlTpPositionMode({
|
||||||
|
symbol,
|
||||||
|
tp_sl_mode: 'Full',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.SAME_SLTP_MODE,
|
||||||
|
ret_msg: 'same tp sl mode2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setMarginType()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setMarginType({
|
||||||
|
symbol,
|
||||||
|
is_isolated: false,
|
||||||
|
buy_leverage: 5,
|
||||||
|
sell_leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setRiskLimit()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setRiskLimit({
|
||||||
|
symbol,
|
||||||
|
risk_id: 'myriskid',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.RISK_LIMIT_NOT_EXISTS,
|
||||||
|
ret_msg: 'risk limit not exists',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,66 +1,93 @@
|
|||||||
import { InverseClient } from "../../src/inverse-client";
|
import { InverseClient } from '../../src/inverse-client';
|
||||||
import { notAuthenticatedError, successResponseList, successResponseObject } from "../response.util";
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Inverse REST API Endpoints', () => {
|
describe('Public Inverse REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const useLivenet = true;
|
||||||
const api = new InverseClient(undefined, undefined, useLivenet, { disable_time_sync: true });
|
const api = new InverseClient(undefined, undefined, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
const timestampOneHourAgo = (new Date().getTime() / 1000) - (1000 * 60 * 60);
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
const from = Number(timestampOneHourAgo.toFixed(0));
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
describe('Inverse only endpoints', () => {
|
describe('Inverse only endpoints', () => {
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
expect(() => api.getPosition()).rejects.toMatchObject(notAuthenticatedError());
|
expect(() => api.getPosition()).rejects.toMatchObject(
|
||||||
});
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
it('getKline()', async () => {
|
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(
|
||||||
expect(
|
notAuthenticatedError()
|
||||||
await api.getKline({ symbol, interval, from })
|
);
|
||||||
).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getTrades()', async () => {
|
|
||||||
expect(await api.getTrades({ symbol })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getIndexPriceKline()', async () => {
|
|
||||||
expect(await api.getIndexPriceKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getPremiumIndexKline()', async () => {
|
|
||||||
expect(await api.getPremiumIndexKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getLastFundingRate()', async () => {
|
|
||||||
expect(await api.getLastFundingRate({ symbol })).toMatchObject(successResponseObject());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Shared endpoints', () => {
|
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
|
||||||
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(notAuthenticatedError());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getOrderBook()', async () => {
|
it('getOrderBook()', async () => {
|
||||||
expect(await api.getOrderBook({ symbol })).toMatchObject(successResponseList());
|
expect(await api.getOrderBook({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getKline()', async () => {
|
||||||
|
expect(await api.getKline({ symbol, interval, from })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getTickers()', async () => {
|
it('getTickers()', async () => {
|
||||||
expect(await api.getTickers()).toMatchObject(successResponseList());
|
expect(await api.getTickers()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('getSymbols()', async () => {
|
it('getSymbols()', async () => {
|
||||||
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getMarkPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getMarkPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getIndexPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPremiumIndexKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getPremiumIndexKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingRate()', async () => {
|
||||||
|
expect(await api.getLastFundingRate({ symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('getServerTime()', async () => {
|
it('getServerTime()', async () => {
|
||||||
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fetchServertime() returns number', async () => {
|
||||||
|
expect(await api.fetchServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
it('getApiAnnouncements()', async () => {
|
it('getApiAnnouncements()', async () => {
|
||||||
expect(await api.getApiAnnouncements()).toMatchObject(successResponseList());
|
expect(await api.getApiAnnouncements()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
103
test/linear/private.read.test.ts
Normal file
103
test/linear/private.read.test.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { LinearClient } from '../../src/linear-client';
|
||||||
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Linear REST API GET Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new LinearClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
|
||||||
|
it('getApiKeyInfo()', async () => {
|
||||||
|
expect(await api.getApiKeyInfo()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletBalance()', async () => {
|
||||||
|
expect(await api.getWalletBalance()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWalletFundRecords()', async () => {
|
||||||
|
expect(await api.getWalletFundRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWithdrawRecords()', async () => {
|
||||||
|
expect(await api.getWithdrawRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetExchangeRecords()', async () => {
|
||||||
|
expect(await api.getAssetExchangeRecords()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getActiveOrderList()', async () => {
|
||||||
|
expect(await api.getActiveOrderList({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryActiveOrder()', async () => {
|
||||||
|
expect(await api.queryActiveOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getConditionalOrder()', async () => {
|
||||||
|
expect(await api.getConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queryConditionalOrder()', async () => {
|
||||||
|
expect(await api.queryConditionalOrder({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPosition()', async () => {
|
||||||
|
expect(await api.getPosition()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTradeRecords()', async () => {
|
||||||
|
expect(await api.getTradeRecords({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getClosedPnl()', async () => {
|
||||||
|
expect(await api.getClosedPnl({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getRiskLimitList()', async () => {
|
||||||
|
expect(await api.getRiskLimitList({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPredictedFundingFee()', async () => {
|
||||||
|
expect(await api.getPredictedFundingFee({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingFee()', async () => {
|
||||||
|
expect(await api.getLastFundingFee({ symbol: symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
230
test/linear/private.write.test.ts
Normal file
230
test/linear/private.write.test.ts
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import { API_ERROR_CODE, LinearClient } from '../../src';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new LinearClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
|
||||||
|
// These tests are primarily check auth is working by expecting balance or order not found style errors
|
||||||
|
|
||||||
|
it('placeActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeActiveOrder({
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
order_type: 'Limit',
|
||||||
|
price: 20000,
|
||||||
|
qty: 1,
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
reduce_only: false,
|
||||||
|
close_on_trigger: false,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_COST_NOT_AVAILABLE,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelActiveOrder({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllActiveOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllActiveOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceActiveOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceActiveOrder({
|
||||||
|
symbol,
|
||||||
|
order_id: '123123123',
|
||||||
|
p_r_qty: 1,
|
||||||
|
p_r_price: 30000,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('placeConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.placeConditionalOrder({
|
||||||
|
order_type: 'Limit',
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
qty: 1,
|
||||||
|
price: 8100,
|
||||||
|
base_price: 8300,
|
||||||
|
stop_px: 8150,
|
||||||
|
time_in_force: 'GoodTillCancel',
|
||||||
|
order_link_id: 'cus_order_id_1',
|
||||||
|
reduce_only: false,
|
||||||
|
trigger_by: 'LastPrice',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR,
|
||||||
|
ret_msg: 'Insufficient wallet balance',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
order_link_id: 'lkasmdflasd',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE_LINEAR,
|
||||||
|
ret_msg: 'order not exists or too late to cancel',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllConditionalOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelAllConditionalOrders({
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaceConditionalOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.replaceConditionalOrder({
|
||||||
|
symbol,
|
||||||
|
p_r_price: 50000,
|
||||||
|
p_r_qty: 1,
|
||||||
|
order_link_id: 'someorderid',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE_LINEAR,
|
||||||
|
ret_msg: 'order not exists or too late to replace',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setAutoAddMargin()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setAutoAddMargin({
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
auto_add_margin: true,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.AUTO_ADD_MARGIN_NOT_MODIFIED,
|
||||||
|
ret_msg: 'autoAddMargin not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setMarginSwitch()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setMarginSwitch({
|
||||||
|
symbol,
|
||||||
|
is_isolated: true,
|
||||||
|
buy_leverage: 5,
|
||||||
|
sell_leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ISOLATED_NOT_MODIFIED_LINEAR,
|
||||||
|
ret_msg: 'Isolated not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setPositionMode()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setPositionMode({
|
||||||
|
symbol,
|
||||||
|
mode: 'BothSide',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_MODE_NOT_MODIFIED,
|
||||||
|
ret_msg: 'position mode not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setPositionTpSlMode()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setPositionTpSlMode({
|
||||||
|
symbol,
|
||||||
|
tp_sl_mode: 'Full',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.SAME_SLTP_MODE_LINEAR,
|
||||||
|
ret_msg: 'same tp sl mode2',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setAddReduceMargin()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setAddReduceMargin({
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
margin: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.POSITION_SIZE_IS_ZERO,
|
||||||
|
ret_msg: 'position size is zero',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setUserLeverage()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setUserLeverage({
|
||||||
|
symbol,
|
||||||
|
buy_leverage: 5,
|
||||||
|
sell_leverage: 5,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.LEVERAGE_NOT_MODIFIED,
|
||||||
|
ret_msg: 'leverage not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setTradingStop()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setTradingStop({
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
take_profit: 555,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.CANNOT_SET_LINEAR_TRADING_STOP_FOR_ZERO_POS,
|
||||||
|
ret_msg: 'can not set tp/sl/ts for zero position',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setRiskLimit()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.setRiskLimit({
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
risk_id: 2,
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.RISK_ID_NOT_MODIFIED,
|
||||||
|
ret_msg: 'risk id not modified',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,66 +1,92 @@
|
|||||||
import { LinearClient } from "../../src/linear-client";
|
import { LinearClient } from '../../src/linear-client';
|
||||||
import { notAuthenticatedError, successResponseList, successResponseObject } from "../response.util";
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Linear REST API Endpoints', () => {
|
describe('Public Linear REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const useLivenet = true;
|
||||||
const api = new LinearClient(undefined, undefined, useLivenet, { disable_time_sync: true });
|
const api = new LinearClient(undefined, undefined, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
const timestampOneHourAgo = (new Date().getTime() / 1000) - (1000 * 60 * 60);
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
const from = Number(timestampOneHourAgo.toFixed(0));
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
describe('Linear only endpoints', () => {
|
describe('Linear only endpoints', () => {
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
expect(() => api.getPosition()).rejects.toMatchObject(notAuthenticatedError());
|
expect(() => api.getPosition()).rejects.toMatchObject(
|
||||||
});
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
it('getKline()', async () => {
|
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(
|
||||||
expect(
|
notAuthenticatedError()
|
||||||
await api.getKline({ symbol, interval, from })
|
);
|
||||||
).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getTrades()', async () => {
|
|
||||||
expect(await api.getTrades({ symbol })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getIndexPriceKline()', async () => {
|
|
||||||
expect(await api.getIndexPriceKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getPremiumIndexKline()', async () => {
|
|
||||||
expect(await api.getPremiumIndexKline({ symbol, interval, from })).toMatchObject(successResponseList());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getLastFundingRate()', async () => {
|
|
||||||
expect(await api.getLastFundingRate({ symbol })).toMatchObject(successResponseObject());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Shared endpoints', () => {
|
|
||||||
it('should throw for unauthenticated private calls', async () => {
|
|
||||||
expect(() => api.getApiKeyInfo()).rejects.toMatchObject(notAuthenticatedError());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getOrderBook()', async () => {
|
it('getOrderBook()', async () => {
|
||||||
expect(await api.getOrderBook({ symbol })).toMatchObject(successResponseList());
|
expect(await api.getOrderBook({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getKline()', async () => {
|
||||||
|
expect(await api.getKline({ symbol, interval, from })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getTickers()', async () => {
|
it('getTickers()', async () => {
|
||||||
expect(await api.getTickers()).toMatchObject(successResponseList());
|
expect(await api.getTickers()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades({ symbol })).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
|
});
|
||||||
it('getSymbols()', async () => {
|
it('getSymbols()', async () => {
|
||||||
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
expect(await api.getSymbols()).toMatchObject(successResponseList());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getMarkPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getMarkPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPriceKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getIndexPriceKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPremiumIndexKline()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getPremiumIndexKline({ symbol, interval, from })
|
||||||
|
).toMatchObject(successResponseList());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingRate()', async () => {
|
||||||
|
expect(await api.getLastFundingRate({ symbol })).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('getServerTime()', async () => {
|
it('getServerTime()', async () => {
|
||||||
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fetchServertime() returns number', async () => {
|
||||||
|
expect(await api.fetchServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
it('getApiAnnouncements()', async () => {
|
it('getApiAnnouncements()', async () => {
|
||||||
expect(await api.getApiAnnouncements()).toMatchObject(successResponseList());
|
expect(await api.getApiAnnouncements()).toMatchObject(
|
||||||
|
successResponseList()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,26 +1,31 @@
|
|||||||
|
export function successResponseList(successMsg: string | null = 'OK') {
|
||||||
export function successResponseList() {
|
|
||||||
return {
|
return {
|
||||||
"ext_code": "",
|
result: expect.any(Array),
|
||||||
"ext_info": "",
|
ret_code: 0,
|
||||||
"result": expect.any(Array),
|
ret_msg: successMsg,
|
||||||
"ret_code": 0,
|
|
||||||
"ret_msg": "OK",
|
|
||||||
"time_now": expect.any(String),
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export function successResponseObject() {
|
export function successResponseObject(successMsg: string | null = 'OK') {
|
||||||
return {
|
return {
|
||||||
"ext_code": "",
|
result: expect.any(Object),
|
||||||
"ext_info": "",
|
ret_code: 0,
|
||||||
"result": expect.any(Object),
|
ret_msg: successMsg,
|
||||||
"ret_code": 0,
|
|
||||||
"ret_msg": "OK",
|
|
||||||
"time_now": expect.any(String),
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export function errorResponseObject(
|
||||||
|
result: null | any = null,
|
||||||
|
ret_code: number,
|
||||||
|
ret_msg: string
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
ret_code,
|
||||||
|
ret_msg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function notAuthenticatedError() {
|
export function notAuthenticatedError() {
|
||||||
return new Error('Private endpoints require api and private keys set');
|
return new Error('Private endpoints require api and private keys set');
|
||||||
};
|
}
|
||||||
|
|||||||
54
test/spot/private.read.test.ts
Normal file
54
test/spot/private.read.test.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { SpotClient } from '../../src';
|
||||||
|
import {
|
||||||
|
errorResponseObject,
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Spot REST API Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new SpotClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const interval = '15m';
|
||||||
|
|
||||||
|
it('getOrder()', async () => {
|
||||||
|
// No auth error == test pass
|
||||||
|
expect(await api.getOrder({ orderId: '123123' })).toMatchObject(
|
||||||
|
errorResponseObject(null, -2013, 'Order does not exist.')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOpenOrders()', async () => {
|
||||||
|
expect(await api.getOpenOrders()).toMatchObject(successResponseList(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPastOrders()', async () => {
|
||||||
|
expect(await api.getPastOrders()).toMatchObject(successResponseList(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMyTrades()', async () => {
|
||||||
|
expect(await api.getMyTrades()).toMatchObject(successResponseList(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject({
|
||||||
|
result: {
|
||||||
|
balances: expect.any(Array),
|
||||||
|
},
|
||||||
|
ret_code: 0,
|
||||||
|
ret_msg: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
56
test/spot/private.write.test.ts
Normal file
56
test/spot/private.write.test.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { API_ERROR_CODE, SpotClient } from '../../src';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new SpotClient(API_KEY, API_SECRET, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
|
||||||
|
// These tests are primarily check auth is working by expecting balance or order not found style errors
|
||||||
|
|
||||||
|
it('submitOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.submitOrder({
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
qty: 10000,
|
||||||
|
type: 'MARKET',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.BALANCE_INSUFFICIENT_SPOT,
|
||||||
|
ret_msg: 'Balance insufficient ',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrder({
|
||||||
|
orderId: '1231231',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE_SPOT,
|
||||||
|
ret_msg: 'Order does not exist.',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrderBatch()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrderBatch({
|
||||||
|
symbol,
|
||||||
|
orderTypes: ['LIMIT', 'LIMIT_MAKER'],
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObject(''));
|
||||||
|
});
|
||||||
|
});
|
||||||
81
test/spot/public.test.ts
Normal file
81
test/spot/public.test.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { SpotClient } from '../../src';
|
||||||
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Spot REST API Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const api = new SpotClient(undefined, undefined, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const interval = '15m';
|
||||||
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
|
expect(() => api.getOpenOrders()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
expect(() => api.getBalances()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbols()', async () => {
|
||||||
|
expect(await api.getSymbols()).toMatchObject(successResponseList(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMergedOrderBook()', async () => {
|
||||||
|
expect(await api.getMergedOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades(symbol)).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCandles()', async () => {
|
||||||
|
expect(await api.getCandles(symbol, interval)).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get24hrTicker()', async () => {
|
||||||
|
expect(await api.get24hrTicker()).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastTradedPrice()', async () => {
|
||||||
|
expect(await api.getLastTradedPrice()).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBestBidAskPrice()', async () => {
|
||||||
|
expect(await api.getBestBidAskPrice()).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetchServertime() returns number', async () => {
|
||||||
|
expect(await api.fetchServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user