Merge branch 'wsapi' into wsapi

This commit is contained in:
Tiago
2025-02-17 14:53:57 +00:00
committed by GitHub
53 changed files with 550 additions and 5368 deletions

View File

@@ -1,314 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIKeyInfoV3,
APIResponseV3WithTime,
APIResponseWithTime,
AccountCoinBalanceResponseV3,
AccountCoinBalancesRequestV3,
AccountCoinBalancesResponseV3,
AssetInfoRequestV3,
AssetInfoResponseV3,
CoinInfoQueryResponseV3,
CreateSubAPIKeyRequestV3,
CreateSubAPIKeyResponseV3,
CreateSubMemberRequestV3,
CreateSubMemberResponseV3,
DepositAddressResponseV3,
DepositRecordQueryRequestV3,
DepositRecordQueryResponseV3,
InternalTransferRequestV3,
ModifyAPIKeyRequestV3,
QueryDepositAddressRequestV3,
QueryInternalTransferSResponseV3,
QueryInternalTransfersRequestV3,
QuerySubAccountDepositAddressRequestV3,
SingleAccountCoinBalanceRequestV3,
SubAccountTransferRequestV3,
SubAccountTransferResponseV3,
SubDepositRecordQueryRequestV3,
SubMemberResponseV3,
SupportedDepositListRequestV3,
SupportedDepositListResponseV3,
TransferCoinListRequestV3,
UniversalTransferCreateResponse,
UniversalTransferListRequestV3,
UniversalTransferListResponseV3,
UniversalTransferRequestV3,
WithdrawCreateRequestV3,
WithdrawRecordQueryRequestV3,
WithdrawRecordsQueryResponseV3,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Account Asset V3 APIs
* @deprecated WARNING
* These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024.
* They may stop working at any point before then.
* Please update your code as soon as possible to use the V5 APIs instead.
*/
export class AccountAssetClientV3 extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time);
}
getServerTime(): Promise<
APIResponseV3WithTime<{ timeSecond: string; timeNano: string }>
> {
return this.get('/v3/public/time');
}
/**
*
* Transfer Data Endpoints
*
*/
createInternalTransfer(
params: InternalTransferRequestV3,
): Promise<APIResponseWithTime<{ transferId: string }>> {
return this.postPrivate(
'/asset/v3/private/transfer/inter-transfer',
params,
);
}
getInternalTransfers(
params: QueryInternalTransfersRequestV3,
): Promise<APIResponseWithTime<QueryInternalTransferSResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/inter-transfer/list/query',
params,
);
}
createSubAccountTransfer(params: {
transferId: string;
coin: string;
amount: string;
subMemberId: number;
type: 'IN' | 'OUT';
}): Promise<APIResponseWithTime<{ transferId: string }>> {
return this.postPrivate(
'/asset/v3/private/transfer/sub-member-transfer',
params,
);
}
getSubAccountTransfers(
params?: SubAccountTransferRequestV3,
): Promise<APIResponseWithTime<SubAccountTransferResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/sub-member-transfer/list/query',
params,
);
}
getSubAccounts(): Promise<
APIResponseWithTime<{
subMemberIds: string[];
transferableSubMemberIds: string[];
}>
> {
return this.getPrivate('/asset/v3/private/transfer/sub-member/list/query');
}
enableUniversalTransfer(params?: {
subMemberIds?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate(
'/asset/v3/private/transfer/transfer-sub-member-save',
params,
);
}
createUniversalTransfer(
params: UniversalTransferRequestV3,
): Promise<APIResponseWithTime<UniversalTransferCreateResponse>> {
return this.postPrivate(
'/asset/v3/private/transfer/universal-transfer',
params,
);
}
getUniversalTransfers(
params: UniversalTransferListRequestV3,
): Promise<APIResponseWithTime<UniversalTransferListResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/universal-transfer/list/query',
params,
);
}
getTransferableCoinList(
params: TransferCoinListRequestV3,
): Promise<APIResponseWithTime<{ list: string[] }>> {
return this.getPrivate(
'/asset/v3/private/transfer/transfer-coin/list/query',
params,
);
}
getAccountCoinBalance(
params: SingleAccountCoinBalanceRequestV3,
): Promise<APIResponseWithTime<AccountCoinBalanceResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/account-coin/balance/query',
params,
);
}
getAccountCoinBalances(
params: AccountCoinBalancesRequestV3,
): Promise<APIResponseWithTime<AccountCoinBalancesResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/account-coins/balance/query',
params,
);
}
getAssetInfo(
params?: AssetInfoRequestV3,
): Promise<APIResponseWithTime<AssetInfoResponseV3>> {
return this.getPrivate(
'/asset/v3/private/transfer/asset-info/query',
params,
);
}
/**
*
* Wallet & Deposit Endpoints
*
*/
/** Get Deposit Spec */
getSupportedDepositList(
params?: SupportedDepositListRequestV3,
): Promise<APIResponseWithTime<SupportedDepositListResponseV3>> {
return this.get(
'/asset/v3/public/deposit/allowed-deposit-list/query',
params,
);
}
getDepositRecords(
params?: DepositRecordQueryRequestV3,
): Promise<APIResponseWithTime<DepositRecordQueryResponseV3>> {
return this.getPrivate('/asset/v3/private/deposit/record/query', params);
}
getSubDepositRecords(
params: SubDepositRecordQueryRequestV3,
): Promise<APIResponseWithTime<DepositRecordQueryResponseV3>> {
return this.getPrivate(
'/asset/v3/private/deposit/sub-member-record/query',
params,
);
}
getWithdrawRecords(
params?: WithdrawRecordQueryRequestV3,
): Promise<APIResponseWithTime<WithdrawRecordsQueryResponseV3>> {
return this.getPrivate('/asset/v3/private/withdraw/record/query', params);
}
getCoinInformation(
coin?: string,
): Promise<APIResponseWithTime<CoinInfoQueryResponseV3>> {
return this.getPrivate('/asset/v3/private/coin-info/query', { coin });
}
submitWithdrawal(
params: WithdrawCreateRequestV3,
): Promise<APIResponseWithTime<{ id: string }>> {
return this.postPrivate('/asset/v3/private/withdraw/create', params);
}
cancelWithdrawal(
withdrawalId: number,
): Promise<APIResponseWithTime<{ status: 1 | 0 }>> {
return this.postPrivate('/asset/v3/private/withdraw/create', {
withdrawalId,
});
}
getMasterAccountDepositAddress(
params?: QueryDepositAddressRequestV3,
): Promise<APIResponseWithTime<DepositAddressResponseV3>> {
return this.getPrivate('/asset/v3/private/deposit/address/query', params);
}
getSubAccountDepositAddress(
params: QuerySubAccountDepositAddressRequestV3,
): Promise<APIResponseWithTime<DepositAddressResponseV3>> {
return this.getPrivate(
'/asset/v3/private/deposit/sub-member-address/query',
params,
);
}
createSubMember(
params: CreateSubMemberRequestV3,
): Promise<APIResponseWithTime<CreateSubMemberResponseV3>> {
return this.postPrivate('/user/v3/private/create-sub-member', params);
}
createSubAPIKey(
params: CreateSubAPIKeyRequestV3,
): Promise<APIResponseWithTime<CreateSubAPIKeyResponseV3>> {
return this.postPrivate('/user/v3/private/create-sub-api', params);
}
/**
* Get Sub UID List
*/
getSubMembers(): Promise<APIResponseWithTime<SubMemberResponseV3>> {
return this.getPrivate('/user/v3/private/query-sub-members');
}
/**
* Froze Sub UID
*/
freezeSubMember(
subuid: number,
frozenStatus: 0 | 1,
): Promise<APIResponseWithTime<{}>> {
return this.postPrivate('/user/v3/private/frozen-sub-member', {
subuid,
frozen: frozenStatus,
});
}
getAPIKeyInformation(): Promise<APIResponseWithTime<APIKeyInfoV3>> {
return this.getPrivate('/user/v3/private/query-api');
}
modifyMasterAPIKey(
params: ModifyAPIKeyRequestV3,
): Promise<APIResponseWithTime<APIKeyInfoV3>> {
return this.postPrivate('/user/v3/private/update-api', params);
}
modifySubAPIKey(
params: ModifyAPIKeyRequestV3,
): Promise<APIResponseWithTime<APIKeyInfoV3>> {
return this.postPrivate('/user/v3/private/update-sub-api', params);
}
/** WARNING: BE CAREFUL! The API key used to call this interface will be invalid immediately. */
deleteMasterAPIKey(): Promise<APIResponseWithTime<{}>> {
return this.postPrivate('/user/v3/private/delete-api');
}
/** WARNING: BE CAREFUL! The API key used to call this interface will be invalid immediately. */
deleteSubAPIKey(): Promise<APIResponseWithTime<{}>> {
return this.postPrivate('/user/v3/private/delete-sub-api');
}
}

View File

@@ -1,153 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseWithTime,
AccountAssetInformationRequest,
DepositRecordsRequest,
EnableUniversalTransferRequest,
InternalTransferRequest,
SubAccountTransferRequest,
SupportedDepositListRequest,
TransferQueryRequest,
UniversalTransferRequest,
WithdrawalRecordsRequest,
WithdrawalRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Account Asset APIs
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class AccountAssetClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.accountAsset;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Transfer Data Endpoints
*
*/
createInternalTransfer(
params: InternalTransferRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/transfer', params);
}
createSubAccountTransfer(
params: SubAccountTransferRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/sub-member/transfer', params);
}
getInternalTransfers(
params?: TransferQueryRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/transfer/list', params);
}
getSubAccountTransfers(
params?: TransferQueryRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate(
'/asset/v1/private/sub-member/transfer/list',
params,
);
}
getSubAccounts(): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/sub-member/member-ids');
}
enableUniversalTransfer(
params?: EnableUniversalTransferRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/transferable-subs/save', params);
}
createUniversalTransfer(
params: UniversalTransferRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/universal/transfer', params);
}
getUniversalTransfers(
params?: TransferQueryRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/universal/transfer/list', params);
}
/**
*
* Wallet & Deposit Endpoints
*
*/
getSupportedDepositList(
params?: SupportedDepositListRequest,
): Promise<APIResponseWithTime<any>> {
return this.get('/asset/v1/public/deposit/allowed-deposit-list', params);
}
getDepositRecords(
params?: DepositRecordsRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/deposit/record/query', params);
}
getWithdrawRecords(
params?: WithdrawalRecordsRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/withdraw/record/query', params);
}
getCoinInformation(coin?: string): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/coin-info/query', { coin });
}
getAssetInformation(
params?: AccountAssetInformationRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/asset-info/query', params);
}
submitWithdrawal(
params: WithdrawalRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/withdraw', params);
}
cancelWithdrawal(withdrawalId: number): Promise<APIResponseWithTime<any>> {
return this.postPrivate('/asset/v1/private/withdraw/cancel', {
id: withdrawalId,
});
}
getDepositAddress(coin: string): Promise<APIResponseWithTime<any>> {
return this.getPrivate('/asset/v1/private/deposit/address', { coin });
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
getApiAnnouncements(): Promise<APIResponseWithTime<any[]>> {
return this.get('/v2/public/announcement');
}
}

View File

@@ -1,364 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
ContractActiveOrdersRequest,
ContractCancelOrderRequest,
ContractClosedPNLRequest,
ContractHistoricOrder,
ContractHistoricOrdersRequest,
ContractListResult,
ContractModifyOrderRequest,
ContractOrderRequest,
ContractPositionsRequest,
ContractSetAutoAddMarginRequest,
ContractSetMarginSwitchRequest,
ContractSetPositionModeRequest,
ContractSetTPSLRequest,
ContractSymbolTicker,
ContractUserExecutionHistoryRequest,
ContractWalletFundRecordRequest,
PaginatedResult,
UMCandlesRequest,
UMCategory,
UMFundingRateHistoryRequest,
UMInstrumentInfoRequest,
UMOpenInterestRequest,
UMOptionDeliveryPriceRequest,
UMPublicTradesRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Derivatives V3 Contract APIs
* @deprecated WARNING
* These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024.
* They may stop working at any point before then.
* Please update your code as soon as possible to use the V5 APIs instead.
*/
export class ContractClient extends BaseRestClient {
getClientType() {
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints : these seem exactly the same as the unified margin market data endpoints
*
*/
/** Query order book info. Each side has a depth of 25 orders. */
getOrderBook(
symbol: string,
category?: string,
limit?: number,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/order-book/L2', {
category,
symbol,
limit,
});
}
/** Get candles/klines */
getCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/kline', params);
}
/** Get a symbol price/statistics ticker */
getSymbolTicker(
category: UMCategory | '',
symbol?: string,
): Promise<APIResponseV3<ContractListResult<ContractSymbolTicker>>> {
return this.get('/derivatives/v3/public/tickers', { category, symbol });
}
/** Get trading rules per symbol/contract, incl price/amount/value/leverage filters */
getInstrumentInfo(
params: UMInstrumentInfoRequest,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/instruments-info', params);
}
/** Query mark price kline (like getCandles() but for mark price). */
getMarkPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/mark-price-kline', params);
}
/** Query Index Price Kline */
getIndexPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/index-price-kline', params);
}
/**
* The funding rate is generated every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC.
* For example, if a request is sent at 12:00 UTC, the funding rate generated earlier that day at 08:00 UTC will be sent.
*/
getFundingRateHistory(
params: UMFundingRateHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.get(
'/derivatives/v3/public/funding/history-funding-rate',
params,
);
}
/** Get Risk Limit */
getRiskLimit(
category: UMCategory,
symbol: string,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/risk-limit/list', {
category,
symbol,
});
}
/** Get option delivery price */
getOptionDeliveryPrice(
params: UMOptionDeliveryPriceRequest,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/delivery-price', params);
}
/** Get public trading history */
getTrades(params: UMPublicTradesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/recent-trade', params);
}
/**
* Gets the total amount of unsettled contracts.
* In other words, the total number of contracts held in open positions.
*/
getOpenInterest(params: UMOpenInterestRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/open-interest', params);
}
/**
*
* Contract Account Endpoints
*
*/
/** -> Order API */
/** Place an order */
submitOrder(params: ContractOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/order/create', params);
}
/**
* Query order history.
*
* As order creation/cancellation is asynchronous, the data returned from the interface may be delayed.
* To access order information in real-time, call getActiveOrders().
*/
getHistoricOrders(
params: ContractHistoricOrdersRequest,
): Promise<APIResponseV3<PaginatedResult<ContractHistoricOrder>>> {
return this.getPrivate('/contract/v3/private/order/list', params);
}
/** Cancel order */
cancelOrder(params: ContractCancelOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/order/cancel', params);
}
/** Cancel all orders */
cancelAllOrders(symbol: string): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/order/cancel-all', {
symbol,
});
}
/**
* Replace order
*
* Active order parameters (such as quantity, price) and stop order parameters
* cannot be modified in one request at the same time.
*
* Please request modification separately.
*/
modifyOrder(params: ContractModifyOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/order/replace', params);
}
/** Query Open Order(s) (real-time) */
getActiveOrders(
params: ContractActiveOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/contract/v3/private/order/unfilled-orders',
params,
);
}
/** -> Positions API */
/**
* Query my positions real-time. Accessing personal list of positions.
* Either symbol or settleCoin is required.
* Users can access their position holding information through this interface, such as the number of position holdings and wallet balance.
*/
getPositions(params?: ContractPositionsRequest): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/position/list', params);
}
/** Set auto add margin, or Auto-Margin Replenishment. */
setAutoAddMargin(
params: ContractSetAutoAddMarginRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/position/set-auto-add-margin',
params,
);
}
/** Switch cross margin mode/isolated margin mode */
setMarginSwitch(
params: ContractSetMarginSwitchRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/position/switch-isolated',
params,
);
}
/** Supports switching between One-Way Mode and Hedge Mode at the coin level. */
setPositionMode(
params: ContractSetPositionModeRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/position/switch-mode',
params,
);
}
/**
* Switch mode between Full or Partial
*/
setTPSLMode(
symbol: string,
tpSlMode: 'Full' | 'Partial',
): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/position/switch-tpsl-mode', {
symbol,
tpSlMode,
});
}
/** Leverage setting. */
setLeverage(
symbol: string,
buyLeverage: string,
sellLeverage: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/position/set-leverage', {
symbol,
buyLeverage,
sellLeverage,
});
}
/**
* Set take profit, stop loss, and trailing stop for your open position.
* If using partial mode, TP/SL/TS orders will not close your entire position.
*/
setTPSL(params: ContractSetTPSLRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/position/trading-stop',
params,
);
}
/** Set risk limit */
setRiskLimit(
symbol: string,
riskId: number,
/** 0-one-way, 1-buy side, 2-sell side */
positionIdx: 0 | 1 | 2,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/position/set-risk-limit', {
symbol,
riskId,
positionIdx,
});
}
/**
* Get user's trading records.
* The results are ordered in descending order (the first item is the latest). Returns records up to 2 years old.
*/
getUserExecutionHistory(
params: ContractUserExecutionHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/execution/list', params);
}
/**
* Get user's closed profit and loss records.
* The results are ordered in descending order (the first item is the latest).
*/
getClosedProfitAndLoss(
params: ContractClosedPNLRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/position/closed-pnl', params);
}
/** Get the information of open interest limit. */
getOpenInterestLimitInfo(symbol: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/position/limit-info', {
symbol,
});
}
/** -> Account API */
/** Query wallet balance */
getBalances(coin?: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/account/wallet/balance', {
coin,
});
}
/** Get user trading fee rate */
getTradingFeeRate(symbol?: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/account/fee-rate', {
symbol,
});
}
/**
* Get wallet fund records.
* This endpoint also shows exchanges from the Asset Exchange,
* where the types for the exchange are ExchangeOrderWithdraw and ExchangeOrderDeposit.
*
* This endpoint returns incomplete information for transfers involving the derivatives wallet.
* Use the account asset API for creating and querying internal transfers.
*/
getWalletFundRecords(
params?: ContractWalletFundRecordRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/contract/v3/private/account/wallet/fund-records',
params,
);
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
}

View File

@@ -1,162 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
CopyTradingCancelOrderRequest,
CopyTradingCloseOrderRequest,
CopyTradingOrderListRequest,
CopyTradingOrderRequest,
CopyTradingTradingStopRequest,
CopyTradingTransferRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for USDC Perpetual APIs
* @deprecated WARNING
* These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024.
* They may stop working at any point before then.
* Please update your code as soon as possible to use the V5 APIs instead.
*/
export class CopyTradingClient extends BaseRestClient {
getClientType() {
// Follows the same authentication mechanism as USDC APIs
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
getSymbols(): Promise<APIResponseV3<any>> {
return this.get('/contract/v3/public/copytrading/symbol/list');
}
/**
*
* Account Data Endpoints
*
*/
/** -> Order API */
/** Create order */
submitOrder(params: CopyTradingOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/order/create',
params,
);
}
/** Set Trading Stop */
setTradingStop(
params: CopyTradingTradingStopRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/order/trading-stop',
params,
);
}
/** Query Order List */
getActiveOrders(
params?: CopyTradingOrderListRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/contract/v3/private/copytrading/order/list',
params,
);
}
/** Cancel order */
cancelOrder(
params: CopyTradingCancelOrderRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/order/cancel',
params,
);
}
/** Close Order.
* This endpoint's rate_limit will decrease by 10 per request;
* ie, one request to this endpoint consumes 10 from the limit allowed per minute.
*/
closeOrder(
params: CopyTradingCloseOrderRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/order/close',
params,
);
}
/** -> Positions API */
/** Position List */
getPositions(symbol?: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/copytrading/position/list', {
symbol,
});
}
/** Close Position */
closePosition(
symbol: string,
positionIdx: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/contract/v3/private/copytrading/position/close', {
symbol,
positionIdx,
});
}
/** Only integers can be set to set the leverage */
setLeverage(
symbol: string,
buyLeverage: string,
sellLeverage: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/position/set-leverage',
{ symbol, buyLeverage, sellLeverage },
);
}
/**
*
* Wallet Data Endpoints
*
*/
/** Get Wallet Balance */
getBalances(): Promise<APIResponseV3<any>> {
return this.getPrivate('/contract/v3/private/copytrading/wallet/balance');
}
/** Transfer */
transfer(params: CopyTradingTransferRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/contract/v3/private/copytrading/wallet/transfer',
params,
);
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
}

View File

@@ -1,16 +1,5 @@
export * from './account-asset-client';
export * from './account-asset-client-v3';
export * from './copy-trading-client';
export * from './inverse-client';
export * from './inverse-futures-client';
export * from './linear-client';
export * from './rest-client-v5';
export * from './spot-client';
export * from './spot-client-v3';
export * from './usdc-option-client';
export * from './usdc-perpetual-client';
export * from './unified-margin-client';
export * from './contract-client';
export * from './websocket-client';
export * from './util/logger';
export * from './util';

View File

@@ -1,341 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { REST_CLIENT_TYPE_ENUM } from './util';
import {
APIResponseWithTime,
AssetExchangeRecordsReq,
CoinParam,
InverseActiveConditionalOrderRequest,
InverseActiveOrdersRequest,
InverseCancelConditionalOrderRequest,
InverseCancelOrderRequest,
InverseChangePositionMarginRequest,
InverseConditionalOrderRequest,
InverseGetClosedPnlRequest,
InverseGetOrderRequest,
InverseGetTradeRecordsRequest,
InverseOrderRequest,
InverseReplaceConditionalOrderRequest,
InverseReplaceOrderRequest,
InverseSetLeverageRequest,
InverseSetMarginTypeRequest,
InverseSetSlTpPositionModeRequest,
InverseSetTradingStopRequest,
SymbolInfo,
SymbolIntervalFromLimitParam,
SymbolLimitParam,
SymbolParam,
SymbolPeriodLimitParam,
WalletFundRecordsReq,
WithdrawRecordsReq,
} from './types';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Inverse Perpetual Futures APIs (v2)
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class InverseClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.inverse;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
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);
}
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);
}
/**
*
* 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');
}
/**
*
* Account Data Endpoints
*
*/
/**
* Active orders
*/
placeActiveOrder(
orderRequest: InverseOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/order/create', orderRequest);
}
getActiveOrderList(
params: InverseActiveOrdersRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/order/list', params);
}
cancelActiveOrder(
params: InverseCancelOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/order/cancel', params);
}
cancelAllActiveOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/order/cancelAll', params);
}
replaceActiveOrder(
params: InverseReplaceOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/order/replace', params);
}
queryActiveOrder(
params: InverseGetOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/order', params);
}
/**
* Conditional orders
*/
placeConditionalOrder(
params: InverseConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
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: InverseActiveConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/stop-order/list', params);
}
cancelConditionalOrder(
params: InverseCancelConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/stop-order/cancel', params);
}
cancelAllConditionalOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/stop-order/cancelAll', params);
}
replaceConditionalOrder(
params: InverseReplaceConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/stop-order/replace', params);
}
queryConditionalOrder(
params: InverseGetOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/stop-order', params);
}
/**
* Position
*/
getPosition(
params?: Partial<SymbolParam>,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/position/list', params);
}
changePositionMargin(
params: InverseChangePositionMarginRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('position/change-position-margin', params);
}
setTradingStop(
params: InverseSetTradingStopRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/position/trading-stop', params);
}
setUserLeverage(
params: InverseSetLeverageRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/position/leverage/save', params);
}
getTradeRecords(
params: InverseGetTradeRecordsRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/execution/list', params);
}
getClosedPnl(
params: InverseGetClosedPnlRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/trade/closed-pnl/list', params);
}
setSlTpPositionMode(
params: InverseSetSlTpPositionModeRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/tpsl/switch-mode', params);
}
setMarginType(
params: InverseSetMarginTypeRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('v2/private/position/switch-isolated', params);
}
/**
* Funding
*/
getLastFundingRate(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.get('v2/public/funding/prev-funding-rate', params);
}
getMyLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/funding/prev-funding', params);
}
getPredictedFunding(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/funding/predicted-funding', params);
}
/**
* LCP Info
*/
getLcpInfo(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/account/lcp', params);
}
}

View File

@@ -1,407 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
import {
APIResponseWithTime,
AssetExchangeRecordsReq,
CoinParam,
SymbolInfo,
SymbolIntervalFromLimitParam,
SymbolLimitParam,
SymbolParam,
SymbolPeriodLimitParam,
WalletFundRecordsReq,
WithdrawRecordsReq,
} from './types/shared';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Inverse Futures APIs (e.g. quarterly futures) (v2)
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class InverseFuturesClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.inverseFutures;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
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
*/
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);
}
/**
*
* 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');
}
/**
*
* Account Data Endpoints
*
*/
/**
* Active orders
*/
placeActiveOrder(orderRequest: {
side: string;
symbol: string;
order_type: string;
qty: number;
price?: number;
time_in_force: string;
take_profit?: number;
stop_loss?: number;
reduce_only?: boolean;
close_on_trigger?: boolean;
order_link_id?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/order/create', orderRequest);
}
getActiveOrderList(params: {
symbol: string;
order_status?: string;
direction?: string;
limit?: number;
cursor?: string;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/order/list', params);
}
cancelActiveOrder(params: {
symbol: string;
order_id?: string;
order_link_id?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/order/cancel', params);
}
cancelAllActiveOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/order/cancelAll', params);
}
replaceActiveOrder(params: {
order_id?: string;
order_link_id?: string;
symbol: string;
p_r_qty?: string;
p_r_price?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/order/replace', params);
}
queryActiveOrder(params: {
order_id?: string;
order_link_id?: string;
symbol: string;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/order', params);
}
/**
* Conditional orders
*/
placeConditionalOrder(params: {
side: string;
symbol: string;
order_type: string;
qty: string;
price?: string;
base_price: string;
stop_px: string;
time_in_force: string;
trigger_by?: string;
close_on_trigger?: boolean;
order_link_id?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/stop-order/create', params);
}
getConditionalOrder(params: {
symbol: string;
stop_order_status?: string;
direction?: string;
limit?: number;
cursor?: string;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/stop-order/list', params);
}
cancelConditionalOrder(params: {
symbol: string;
stop_order_id?: string;
order_link_id?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/stop-order/cancel', params);
}
cancelAllConditionalOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/stop-order/cancelAll', params);
}
replaceConditionalOrder(params: {
stop_order_id?: string;
order_link_id?: string;
symbol: string;
p_r_qty?: number;
p_r_price?: string;
p_r_trigger_price?: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/stop-order/replace', params);
}
queryConditionalOrder(params: {
symbol: string;
stop_order_id?: string;
order_link_id?: string;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/stop-order', params);
}
/**
* Position
*/
/**
* Get position list
*/
getPosition(
params?: Partial<SymbolParam>,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/position/list', params);
}
changePositionMargin(params: {
symbol: string;
margin: string;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate(
'futures/private/position/change-position-margin',
params,
);
}
setTradingStop(params: {
symbol: string;
take_profit?: number;
stop_loss?: number;
trailing_stop?: number;
tp_trigger_by?: string;
sl_trigger_by?: string;
new_trailing_active?: number;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/position/trading-stop', params);
}
setUserLeverage(params: {
symbol: string;
buy_leverage: number;
sell_leverage: number;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/position/leverage/save', params);
}
/**
* Position mode switch
*/
setPositionMode(params: {
symbol: string;
mode: number;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/position/switch-mode', params);
}
/**
* Cross/Isolated margin switch. Must set leverage value when switching.
*/
setMarginType(params: {
symbol: string;
is_isolated: boolean;
buy_leverage: number;
sell_leverage: number;
}): Promise<APIResponseWithTime<any>> {
return this.postPrivate('futures/private/position/switch-isolated', params);
}
getTradeRecords(params: {
order_id?: string;
symbol: string;
start_time?: number;
page?: number;
limit?: number;
order?: string;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/execution/list', params);
}
getClosedPnl(params: {
symbol: string;
start_time?: number;
end_time?: number;
exec_type?: string;
page?: number;
limit?: number;
}): Promise<APIResponseWithTime<any>> {
return this.getPrivate('futures/private/trade/closed-pnl/list', params);
}
/**
* Funding
*/
getLastFundingRate(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.get('v2/public/funding/prev-funding-rate', params);
}
getMyLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/funding/prev-funding', params);
}
getPredictedFunding(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/funding/predicted-funding', params);
}
/**
* LCP Info
*/
getLcpInfo(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('v2/private/account/lcp', params);
}
}

View File

@@ -1,391 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
import {
APIResponse,
APIResponseWithTime,
AssetExchangeRecordsReq,
CoinParam,
LinearCancelConditionalOrderRequest,
LinearCancelOrderRequest,
LinearConditionalOrderRequest,
LinearGetClosedPnlRequest,
LinearGetConditionalOrderRequest,
LinearGetHistoryTradeRecordsRequest,
LinearGetOrderRequest,
LinearGetOrdersRequest,
LinearGetTradeRecordsRequest,
LinearOrder,
LinearQueryConditionalOrderRequest,
LinearReplaceConditionalOrderRequest,
LinearReplaceOrderRequest,
LinearSetAddReduceMarginRequest,
LinearSetAutoAddMarginRequest,
LinearSetMarginSwitchRequest,
LinearSetPositionModeRequest,
LinearSetPositionTpSlModeRequest,
LinearSetRiskLimitRequest,
LinearSetTradingStopRequest,
LinearSetUserLeverageRequest,
NewLinearOrder,
PerpPosition,
PerpPositionRoot,
SymbolInfo,
SymbolIntervalFromLimitParam,
SymbolLimitParam,
SymbolParam,
SymbolPeriodLimitParam,
WalletBalances,
WalletFundRecordsReq,
WithdrawRecordsReq,
} from './types';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for linear/USD perpetual futures APIs (v2)
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class LinearClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.linear;
}
async fetchServerTime(): Promise<number> {
const timeRes = await this.getServerTime();
return Number(timeRes.time_now);
}
/**
*
* Market Data Endpoints
*
*/
getOrderBook(params: SymbolParam): Promise<APIResponseWithTime<any[]>> {
return this.get('v2/public/orderBook/L2', params);
}
getKline(
params: SymbolIntervalFromLimitParam,
): Promise<APIResponseWithTime<any[]>> {
return this.get('public/linear/kline', params);
}
/**
* Get latest information for symbol
*/
getTickers(
params?: Partial<SymbolParam>,
): Promise<APIResponseWithTime<any[]>> {
return this.get('v2/public/tickers', params);
}
getTrades(params: SymbolLimitParam): Promise<APIResponseWithTime<any[]>> {
return this.get('public/linear/recent-trading-records', params);
}
getSymbols(): Promise<APIResponse<SymbolInfo[]>> {
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(
params: SymbolIntervalFromLimitParam,
): Promise<APIResponseWithTime<any[]>> {
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<WalletBalances>> {
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');
}
/**
*
* Account Data Endpoints
*
*/
placeActiveOrder(
params: NewLinearOrder,
): Promise<APIResponseWithTime<LinearOrder | null>> {
return this.postPrivate('private/linear/order/create', params);
}
getActiveOrderList(
params: LinearGetOrdersRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/order/list', params);
}
cancelActiveOrder(
params: LinearCancelOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/order/cancel', params);
}
cancelAllActiveOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/order/cancel-all', params);
}
replaceActiveOrder(
params: LinearReplaceOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/order/replace', params);
}
queryActiveOrder(
params: LinearGetOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/order/search', params);
}
/**
* Conditional orders
*/
placeConditionalOrder(
params: LinearConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/stop-order/create', params);
}
getConditionalOrder(
params: LinearGetConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/stop-order/list', params);
}
cancelConditionalOrder(
params: LinearCancelConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/stop-order/cancel', params);
}
cancelAllConditionalOrders(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/stop-order/cancel-all', params);
}
replaceConditionalOrder(
params: LinearReplaceConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/stop-order/replace', params);
}
queryConditionalOrder(
params: LinearQueryConditionalOrderRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/stop-order/search', params);
}
/**
* Position
*/
getPosition(): Promise<APIResponseWithTime<PerpPositionRoot[]>>;
getPosition(
params: Partial<SymbolParam>,
): Promise<APIResponseWithTime<PerpPosition[]>>;
getPosition(
params?: Partial<SymbolParam>,
): Promise<APIResponseWithTime<PerpPosition[] | PerpPositionRoot[]>> {
return this.getPrivate('private/linear/position/list', params);
}
setAutoAddMargin(
params?: LinearSetAutoAddMarginRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate(
'private/linear/position/set-auto-add-margin',
params,
);
}
setMarginSwitch(
params?: LinearSetMarginSwitchRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/switch-isolated', params);
}
/**
* Switch between one-way vs hedge mode. Use `linearPositionModeEnum` for the mode parameter.
*/
setPositionMode(
params: LinearSetPositionModeRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/switch-mode', params);
}
/**
* Switch TP/SL mode between full or partial. When set to Partial, TP/SL orders may have a quantity less than the position size.
* This is set with the setTradingStop() method. Use `positionTpSlModeEnum` for the tp_sl_mode parameter.
*/
setPositionTpSlMode(
params: LinearSetPositionTpSlModeRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/tpsl/switch-mode', params);
}
setAddReduceMargin(
params?: LinearSetAddReduceMarginRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/add-margin', params);
}
setUserLeverage(
params: LinearSetUserLeverageRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/set-leverage', params);
}
setTradingStop(
params: LinearSetTradingStopRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/trading-stop', params);
}
getTradeRecords(
params: LinearGetTradeRecordsRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/trade/execution/list', params);
}
getHistoryTradeRecords(
params: LinearGetHistoryTradeRecordsRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate(
'/private/linear/trade/execution/history-list',
params,
);
}
getClosedPnl(
params: LinearGetClosedPnlRequest,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/trade/closed-pnl/list', params);
}
/**
* Risk Limit
*/
getRiskLimitList(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('public/linear/risk-limit', params);
}
setRiskLimit(
params: LinearSetRiskLimitRequest,
): Promise<APIResponseWithTime<any>> {
return this.postPrivate('private/linear/position/set-risk', params);
}
/**
* Funding
*/
getPredictedFundingFee(
params: SymbolParam,
): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/funding/predicted-funding', params);
}
getLastFundingFee(params: SymbolParam): Promise<APIResponseWithTime<any>> {
return this.getPrivate('private/linear/funding/prev-funding', params);
}
}

View File

@@ -57,7 +57,7 @@ import {
DeleteSubMemberParamsV5,
DeliveryPriceV5,
DeliveryRecordV5,
DepositAddressResultV5,
DepositAddressChainV5,
DepositRecordV5,
ExchangeBrokerAccountInfoV5,
ExchangeBrokerEarningResultV5,
@@ -272,7 +272,7 @@ export class RestClientV5 extends BaseRestClient {
*/
getClientType() {
return REST_CLIENT_TYPE_ENUM.v3;
return REST_CLIENT_TYPE_ENUM.v5;
}
async fetchServerTime(): Promise<number> {
@@ -1489,7 +1489,12 @@ export class RestClientV5 extends BaseRestClient {
getMasterDepositAddress(
coin: string,
chainType?: string,
): Promise<APIResponseV3WithTime<DepositAddressResultV5>> {
): Promise<
APIResponseV3WithTime<{
coin: string;
chains: DepositAddressChainV5[];
}>
> {
return this.getPrivate('/v5/asset/deposit/query-address', {
coin,
chainType,
@@ -1503,7 +1508,12 @@ export class RestClientV5 extends BaseRestClient {
coin: string,
chainType: string,
subMemberId: string,
): Promise<APIResponseV3WithTime<DepositAddressResultV5>> {
): Promise<
APIResponseV3WithTime<{
coin: string;
chains: DepositAddressChainV5;
}>
> {
return this.getPrivate('/v5/asset/deposit/query-sub-member-address', {
coin,
chainType,
@@ -1514,6 +1524,7 @@ export class RestClientV5 extends BaseRestClient {
/**
* @deprecated - duplicate function, use getSubDepositAddress() instead
* Query the deposit address information of SUB account.
* @deprecated Duplicate endpoint - Use getSubDepositAddress() instead
*
* CAUTION
* Can use master UID's api key only
@@ -1522,7 +1533,12 @@ export class RestClientV5 extends BaseRestClient {
coin: string,
chainType: string,
subMemberId: string,
): Promise<APIResponseV3<DepositAddressResultV5>> {
): Promise<
APIResponseV3<{
coin: string;
chains: DepositAddressChainV5;
}>
> {
return this.getPrivate('/v5/asset/deposit/query-sub-member-address', {
coin,
chainType,

View File

@@ -1,17 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
KlineInterval,
NewSpotOrderV3,
SpotBalances,
SpotCancelOrderBatchRequest,
SpotCrossMarginBorrowingInfoRequest,
SpotCrossMarginRepaymentHistoryRequest,
SpotLeveragedTokenPRHistoryRequest,
SpotMyTradesRequest,
SpotOrderQueryById,
} from './types';
import { APIResponseV3, numberInString } from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
@@ -24,8 +12,8 @@ import BaseRestClient from './util/BaseRestClient';
*/
export class SpotClientV3 extends BaseRestClient {
getClientType() {
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
return REST_CLIENT_TYPE_ENUM.v3;
// Doesn't really matter here, since the only remaining endpoint does not require auth.
return REST_CLIENT_TYPE_ENUM.v5;
}
async fetchServerTime(): Promise<number> {
@@ -39,17 +27,11 @@ export class SpotClientV3 extends BaseRestClient {
*
*/
/** Get all symbols */
getSymbols(): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/symbols');
}
/** Get orderbook for symbol */
getOrderBook(symbol: string, limit?: number): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/depth', { symbol, limit });
}
/** Get merged orderbook for symbol */
/**
* Get merged orderbook for symbol
*
* This is the only known pre-V5 endpoint to still be online.
*/
getMergedOrderBook(
symbol: string,
scale?: number,
@@ -62,246 +44,13 @@ export class SpotClientV3 extends BaseRestClient {
});
}
/** Get public trading records (raw trades) */
getTrades(symbol: string, limit?: number): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/trades', { symbol, limit });
}
/** Get candles/klines */
getCandles(
symbol: string,
interval: KlineInterval,
limit?: number,
startTime?: number,
endTime?: number,
): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/kline', {
symbol,
interval,
limit,
startTime,
endTime,
});
}
/** Get latest information for symbol (24hr ticker) */
get24hrTicker(symbol?: string): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/ticker/24hr', { symbol });
}
/** Get last traded price */
getLastTradedPrice(symbol?: string): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/ticker/price', { symbol });
}
/** Get best bid/ask price */
getBestBidAskPrice(symbol?: string): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/quote/ticker/bookTicker', { symbol });
}
/**
*
* Account Data Endpoints
*
*/
/** -> Order API */
/** Create order */
submitOrder(params: NewSpotOrderV3): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/order', params);
}
/** Get active order state */
getOrder(params: SpotOrderQueryById): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/order', params);
}
/** Cancel order */
cancelOrder(params: SpotOrderQueryById): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/cancel-order', params);
}
/** Batch cancel orders */
cancelOrderBatch(
params: SpotCancelOrderBatchRequest,
): Promise<APIResponseV3<any>> {
const orderTypes = params.orderTypes
? params.orderTypes.join(',')
: undefined;
return this.postPrivate('/spot/v3/private/cancel-orders', {
...params,
orderTypes,
});
}
/** Batch cancel up to 100 orders by ID */
cancelOrderBatchIDs(orderIds: string[]): Promise<APIResponseV3<any>> {
const orderIdsCsv = orderIds.join(',');
return this.postPrivate('/spot/v3/private/cancel-orders-by-ids', {
orderIds: orderIdsCsv,
});
}
/** Get open orders */
getOpenOrders(
symbol?: string,
orderId?: string,
limit?: number,
orderCategory?: 0 | 1,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/open-orders', {
symbol,
orderId,
limit,
orderCategory,
});
}
/** Get order history */
getPastOrders(
symbol?: string,
orderId?: string,
limit?: number,
orderCategory?: 0 | 1,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/history-orders', {
symbol,
orderId,
limit,
orderCategory,
});
}
/**
* Get your trade history.
* If startTime is not specified, you can only query for records in the last 7 days.
* If you want to query for records older than 7 days, startTime is required.
*/
getMyTrades(params?: SpotMyTradesRequest): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/my-trades', params);
}
/**
*
* Wallet Data Endpoints
*
*/
/** Get Wallet Balance */
getBalances(): Promise<APIResponseV3<SpotBalances>> {
return this.getPrivate('/spot/v3/private/account');
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
getServerTime(): Promise<{ time_now: numberInString }> {
return this.get('/v2/public/time');
}
/**
*
* Leveraged Token Endpoints
*
*/
/** Get all asset infos */
getLeveragedTokenAssetInfos(ltCode?: string): Promise<APIResponseV3<any>> {
return this.get('/spot/v3/public/infos', { ltCode });
}
/** Get leveraged token market info */
getLeveragedTokenMarketInfo(ltCode: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/reference', { ltCode });
}
/** Purchase leveraged token */
purchaseLeveragedToken(
ltCode: string,
ltAmount: string,
serialNo?: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/purchase', {
ltCode,
ltAmount,
serialNo,
});
}
/** Redeem leveraged token */
redeemLeveragedToken(
ltCode: string,
ltAmount: string,
serialNo?: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/redeem', {
ltCode,
ltAmount,
serialNo,
});
}
/** Get leveraged token purchase/redemption history */
getLeveragedTokenPRHistory(
params?: SpotLeveragedTokenPRHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/record', params);
}
/**
*
* Cross Margin Trading Endpoints
*
*/
/** Borrow margin loan */
borrowCrossMarginLoan(
coin: string,
qty: string,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/cross-margin-loan', {
coin,
qty,
});
}
/** Repay margin loan */
repayCrossMarginLoan(coin: string, qty: string): Promise<APIResponseV3<any>> {
return this.postPrivate('/spot/v3/private/cross-margin-repay', {
coin,
qty,
});
}
/** Query borrowing info */
getCrossMarginBorrowingInfo(
params?: SpotCrossMarginBorrowingInfoRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/cross-margin-orders', params);
}
/** Query account info */
getCrossMarginAccountInfo(): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/cross-margin-account');
}
/** Query interest & quota */
getCrossMarginInterestQuota(coin: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/spot/v3/private/cross-margin-loan-info', { coin });
}
/** Query repayment history */
getCrossMarginRepaymentHistory(
params?: SpotCrossMarginRepaymentHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/spot/v3/private/cross-margin-repay-history',
params,
);
}
}

View File

@@ -1,183 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponse,
KlineInterval,
NewSpotOrder,
OrderSide,
OrderTypeSpot,
SpotBalances,
SpotLastPrice,
SpotOrderQueryById,
SpotSymbolInfo,
} from './types';
import BaseRestClient from './util/BaseRestClient';
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
/**
* REST API client for Spot APIs (v1)
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class SpotClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.spot;
}
fetchServerTime(): Promise<number> {
return this.getServerTime();
}
async getServerTime(): Promise<number> {
const res = await this.get('/spot/v1/time');
return res.result.serverTime;
}
/**
*
* Market Data Endpoints
*
**/
getSymbols(): Promise<APIResponse<SpotSymbolInfo[]>> {
return this.get('/spot/v1/symbols');
}
getOrderBook(symbol: string, limit?: number): Promise<APIResponse<any>> {
return this.get('/spot/quote/v1/depth', {
symbol,
limit,
});
}
getMergedOrderBook(
symbol: string,
scale?: number,
limit?: number,
): Promise<APIResponse<any>> {
return this.get('/spot/quote/v1/depth/merged', {
symbol,
scale,
limit,
});
}
getTrades(symbol: string, limit?: number): Promise<APIResponse<any[]>> {
return this.get('/spot/quote/v1/trades', {
symbol,
limit,
});
}
getCandles(
symbol: string,
interval: KlineInterval,
limit?: number,
startTime?: number,
endTime?: number,
): Promise<APIResponse<any[]>> {
return this.get('/spot/quote/v1/kline', {
symbol,
interval,
limit,
startTime,
endTime,
});
}
get24hrTicker(symbol?: string): Promise<APIResponse<any>> {
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
}
getLastTradedPrice(): Promise<APIResponse<SpotLastPrice[]>>;
getLastTradedPrice(symbol: string): Promise<APIResponse<SpotLastPrice>>;
getLastTradedPrice(
symbol?: string,
): Promise<APIResponse<SpotLastPrice | SpotLastPrice[]>> {
return this.get('/spot/quote/v1/ticker/price', { symbol });
}
getBestBidAskPrice(symbol?: string): Promise<APIResponse<any>> {
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
}
/**
* Account Data Endpoints
*/
submitOrder(params: NewSpotOrder): Promise<APIResponse<any>> {
return this.postPrivate('/spot/v1/order', params);
}
getOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
return this.getPrivate('/spot/v1/order', params);
}
cancelOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
return this.deletePrivate('/spot/v1/order', params);
}
cancelOrderBatch(params: {
symbol: string;
side?: OrderSide;
orderTypes: OrderTypeSpot[];
}): Promise<APIResponse<any>> {
const orderTypes = params.orderTypes
? params.orderTypes.join(',')
: undefined;
return this.deletePrivate('/spot/order/batch-cancel', {
...params,
orderTypes,
});
}
getOpenOrders(
symbol?: string,
orderId?: string,
limit?: number,
): Promise<APIResponse<any>> {
return this.getPrivate('/spot/v1/open-orders', {
symbol,
orderId,
limit,
});
}
getPastOrders(
symbol?: string,
orderId?: string,
limit?: number,
): Promise<APIResponse<any>> {
return this.getPrivate('/spot/v1/history-orders', {
symbol,
orderId,
limit,
});
}
getMyTrades(
symbol?: string,
limit?: number,
fromId?: number,
toId?: number,
): Promise<APIResponse<any>> {
return this.getPrivate('/spot/v1/myTrades', {
symbol,
limit,
fromId,
toId,
});
}
/**
* Wallet Data Endpoints
*/
getBalances(): Promise<APIResponse<SpotBalances>> {
return this.getPrivate('/spot/v1/account');
}
}

View File

@@ -3,7 +3,6 @@ export * from './copy-trading';
export * from './contract';
export * from './linear';
export * from './inverse';
export * from './spot';
export * from './usdc-perp';
export * from './usdc-options';
export * from './usdc-shared';

View File

@@ -1,89 +0,0 @@
import { OrderSide, numberInString } from '../shared';
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
export interface NewSpotOrder {
symbol: string;
qty: number;
side: OrderSide;
type: OrderTypeSpot;
timeInForce?: OrderTimeInForce;
price?: number;
orderLinkId?: string;
}
export interface NewSpotOrderV3 {
symbol: string;
orderQty: string;
side: OrderSide;
orderType: OrderTypeSpot;
timeInForce?: OrderTimeInForce;
orderPrice?: string;
orderLinkId?: string;
orderCategory?: 0 | 1;
triggerPrice?: string;
}
export interface SpotCancelOrderBatchRequest {
symbol: string;
side?: OrderSide;
orderTypes: OrderTypeSpot[];
orderCategory?: 0 | 1;
}
export interface SpotOrderQueryById {
orderId?: string;
orderLinkId?: string;
orderCategory?: 0 | 1;
}
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;
}
export interface SpotMyTradesRequest {
symbol?: string;
orderId?: string;
limit?: string;
startTime?: number;
endTime?: number;
fromTradeId?: string;
toTradeId?: string;
}
export interface SpotLeveragedTokenPRHistoryRequest {
ltCode?: string;
orderId?: string;
startTime?: number;
endTime?: number;
limit?: number;
orderType?: 1 | 2;
serialNo?: string;
}
export interface SpotCrossMarginBorrowingInfoRequest {
startTime?: number;
endTime?: number;
coin?: string;
status?: 0 | 1 | 2;
limit?: number;
}
export interface SpotCrossMarginRepaymentHistoryRequest {
startTime?: number;
endTime?: number;
coin?: string;
limit?: number;
}

View File

@@ -10,6 +10,8 @@ export interface GetCoinExchangeRecordParamsV5 {
export interface GetDeliveryRecordParamsV5 {
category: CategoryV5;
symbol?: string;
startTime?: number;
endTime?: number;
expDate?: string;
limit?: number;
cursor?: string;

View File

@@ -1,23 +1,7 @@
import { ContractClient } from '../contract-client';
import { InverseClient } from '../inverse-client';
import { LinearClient } from '../linear-client';
import { RestClientV5 } from '../rest-client-v5';
import { SpotClient } from '../spot-client';
import { SpotClientV3 } from '../spot-client-v3';
import { UnifiedMarginClient } from '../unified-margin-client';
import { USDCOptionClient } from '../usdc-option-client';
import { USDCPerpetualClient } from '../usdc-perpetual-client';
export type RESTClient =
| InverseClient
| LinearClient
| SpotClient
| SpotClientV3
| USDCOptionClient
| USDCPerpetualClient
| UnifiedMarginClient
| ContractClient
| RestClientV5;
export type RESTClient = SpotClientV3 | RestClientV5;
export type numberInString = string;
@@ -53,14 +37,6 @@ export type KlineIntervalV3 =
| 'W'
| 'M';
export interface APIResponse<T> {
ret_code: number;
ret_msg: 'OK' | string;
ext_code: string | null;
ext_info: string | null;
result: T;
}
export interface APIRateLimit {
/** Remaining requests to this endpoint before the next reset */
remainingRequests: number;
@@ -88,12 +64,6 @@ export interface APIResponseV3<T> {
}
export type APIResponseV3WithTime<T> = APIResponseV3<T> & { time: number };
export interface APIResponseWithTime<T = {}> extends APIResponse<T> {
/** UTC timestamp */
time_now: numberInString;
}
/**
* Request Parameter Types
*/

View File

@@ -202,6 +202,7 @@ export interface WSExecutionV5 {
execId: string;
execPrice: string;
execQty: string;
execPnl: string;
execType: ExecTypeV5;
execValue: string;
execTime: string;

View File

@@ -1,18 +1,7 @@
import { RestClientOptions, WS_KEY_MAP } from '../../util';
/** For spot markets, spotV3 is recommended */
export type APIMarket =
| 'inverse'
| 'linear'
| 'spot'
| 'spotv3'
| 'usdcOption'
| 'usdcPerp'
| 'unifiedPerp'
| 'unifiedOption'
| 'contractUSDT'
| 'contractInverse'
| 'v5';
export type APIMarket = 'v5';
// Same as inverse futures
export type WsPublicInverseTopic =
@@ -111,7 +100,7 @@ export interface WSClientConfigurableOptions {
/**
* The API group this client should connect to. The V5 market is currently used by default.
*
* For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account asset/copy trading)
* Only the "V5" "market" is supported here.
*/
market?: APIMarket;
@@ -128,11 +117,21 @@ export interface WSClientConfigurableOptions {
reconnectTimeout?: number;
restOptions?: RestClientOptions;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
requestOptions?: any;
wsUrl?: string;
/**
* Default: false.
*
* When enabled, any calls to the subscribe method will return a promise.
* Note: internally, subscription requests are sent in batches. This may not behave as expected when
* subscribing to a large number of topics, especially if you are not yet connected when subscribing.
*/
promiseSubscribeRequests?: boolean;
/**
* Allows you to provide a custom "signMessage" function, e.g. to use node's much faster createHmac method
*

View File

@@ -1,401 +0,0 @@
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
InternalTransferRequest,
UM7DayTradingHistoryRequest,
UMActiveOrdersRequest,
UMBatchOrder,
UMBatchOrderCancel,
UMBatchOrderReplace,
UMBorrowHistoryRequest,
UMCancelAllOrdersRequest,
UMCancelOrderRequest,
UMCandlesRequest,
UMCategory,
UMExchangeCoinsRequest,
UMFundingRateHistoryRequest,
UMHistoricOrder,
UMHistoricOrdersRequest,
UMInstrumentInfo,
UMInstrumentInfoRequest,
UMModifyOrderRequest,
UMOpenInterestRequest,
UMOptionDeliveryPriceRequest,
UMOptionsSettlementHistoryRequest,
UMOrderRequest,
UMPaginatedResult,
UMPerpSettlementHistoryRequest,
UMPositionsRequest,
UMPublicTradesRequest,
UMSetTPSLRequest,
UMTransactionLogRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for Derivatives V3 unified margin APIs
* @deprecated WARNING
* These endpoints are being switched off gradually and are expected to be completely turned off by the end of 2024.
* They may stop working at any point before then.
* Please update your code as soon as possible to use the V5 APIs instead.
*/
export class UnifiedMarginClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
/** Query order book info. Each side has a depth of 25 orders. */
getOrderBook(
symbol: string,
category: string,
limit?: number,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/order-book/L2', {
category,
symbol,
limit,
});
}
/** Get candles/klines */
getCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/kline', params);
}
/** Get a symbol price/statistics ticker */
getSymbolTicker(
category: UMCategory,
symbol?: string,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/tickers', { category, symbol });
}
/** Get trading rules per symbol/contract, incl price/amount/value/leverage filters */
getInstrumentInfo(
params: UMInstrumentInfoRequest,
): Promise<APIResponseV3<UMPaginatedResult<UMInstrumentInfo>>> {
return this.get('/derivatives/v3/public/instruments-info', params);
}
/** Query mark price kline (like getCandles() but for mark price). */
getMarkPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/mark-price-kline', params);
}
/** Query Index Price Kline */
getIndexPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/index-price-kline', params);
}
/**
* The funding rate is generated every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC.
* For example, if a request is sent at 12:00 UTC, the funding rate generated earlier that day at 08:00 UTC will be sent.
*/
getFundingRateHistory(
params: UMFundingRateHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.get(
'/derivatives/v3/public/funding/history-funding-rate',
params,
);
}
/** Get Risk Limit */
getRiskLimit(
category: UMCategory,
symbol: string,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/risk-limit/list', {
category,
symbol,
});
}
/** Get option delivery price */
getOptionDeliveryPrice(
params: UMOptionDeliveryPriceRequest,
): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/delivery-price', params);
}
/** Get recent trades */
getTrades(params: UMPublicTradesRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/recent-trade', params);
}
/**
* Gets the total amount of unsettled contracts.
* In other words, the total number of contracts held in open positions.
*/
getOpenInterest(params: UMOpenInterestRequest): Promise<APIResponseV3<any>> {
return this.get('/derivatives/v3/public/open-interest', params);
}
/**
*
* Unified Margin Account Endpoints
*
*/
/** -> Order API */
/** Place an order */
submitOrder(params: UMOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/create', params);
}
/** Active order parameters (such as quantity, price) and stop order parameters cannot be modified in one request at the same time. Please request modification separately. */
modifyOrder(params: UMModifyOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/replace', params);
}
/** Cancel order */
cancelOrder(params: UMCancelOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/cancel', params);
}
/** Query Open Orders */
getActiveOrders(params: UMActiveOrdersRequest): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/order/unfilled-orders', params);
}
/** Query order history. As order creation/cancellation is asynchronous, the data returned from the interface may be delayed. To access order information in real-time, call getActiveOrders() */
getHistoricOrders(
params: UMHistoricOrdersRequest,
): Promise<APIResponseV3<UMPaginatedResult<UMHistoricOrder>>> {
return this.getPrivate('/unified/v3/private/order/list', params);
}
/**
* This API provides the batch order mode under the unified margin account.
* Max 10 per request
*/
batchSubmitOrders(
category: UMCategory,
orders: UMBatchOrder[],
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/create-batch', {
category,
request: orders,
});
}
/**
* This interface can modify the open order information in batches.
* Currently, it is not supported to modify the conditional order information.
* Please note that only unfilled or partial filled orders can be modified.
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type
*/
batchReplaceOrders(
category: UMCategory,
orders: UMBatchOrderReplace[],
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/replace-batch', {
category,
request: orders,
});
}
/**
* This API provides batch cancellation under the unified margin account.
* Order cancellation of futures and options cannot be canceled in one request at the same time.
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type.
*/
batchCancelOrders(
category: UMCategory,
orders: UMBatchOrderCancel[],
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/cancel-batch', {
category,
request: orders,
});
}
/**
* This API provides the cancellation of all open orders under the unified margin account.
* Order cancellation of futures and options cannot be canceled in one request at the same time.
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type.
*/
cancelAllOrders(
params: UMCancelAllOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/order/cancel-all', params);
}
/** -> Positions API */
/**
* Query my positions real-time. Accessing personal list of positions.
* Users can access their position holding information through this interface, such as the number of position holdings and wallet balance.
*/
getPositions(params: UMPositionsRequest): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/position/list', params);
}
/** Leverage setting. */
setLeverage(
category: UMCategory,
symbol: string,
buyLeverage: number,
sellLeverage: number,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/position/set-leverage', {
category,
symbol,
buyLeverage,
sellLeverage,
});
}
/**
* Switching the TP/SL mode to the cross margin mode or selected positions.
* When you set the TP/SL mode on the selected positions, the quantity of take-profit or stop-loss orders can be smaller than the position size. Please use Trading-Stop endpoint.
*/
setTPSLMode(
category: UMCategory,
symbol: string,
tpSlMode: 1 | 0,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/position/tpsl/switch-mode', {
category,
symbol,
tpSlMode,
});
}
/** Set risk limit */
setRiskLimit(
category: UMCategory,
symbol: string,
riskId: number,
positionIdx: number,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/unified/v3/private/position/set-risk-limit', {
category,
symbol,
riskId,
positionIdx,
});
}
/**
* Set position TP/SL and trailing stop.
* Pass the following parameters, then the system will create conditional orders.
* If the position is closed, the system will cancel these orders, and adjust the position size.
*/
setTPSL(params: UMSetTPSLRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/unified/v3/private/position/trading-stop',
params,
);
}
/**
* Access the user's filled history, ranked by time in descending order.
* There might be multiple filled histories for an order.
*/
get7DayTradingHistory(
params: UM7DayTradingHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/execution/list', params);
}
/** Query the settlement history, ranked by time in descending order. */
getOptionsSettlementHistory(
params: UMOptionsSettlementHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/delivery-record', params);
}
/** Query session settlement records, only for USDC perpetual */
getUSDCPerpetualSettlementHistory(
params: UMPerpSettlementHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/settlement-record', params);
}
/** -> Account API */
/** Query wallet balance */
getBalances(coin?: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/account/wallet/balance', {
coin,
});
}
/**
* Upgrade to unified margin account.
* WARNING: This is currently not reversable!
*/
upgradeToUnifiedMargin(): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/unified/v3/private/account/upgrade-unified-account',
);
}
/** Query trading history */
getTransactionLog(
params: UMTransactionLogRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/unified/v3/private/account/transaction-log',
params,
);
}
/** Fund transfer between accounts (v2) */
transferFunds(params: InternalTransferRequest): Promise<APIResponseV3<any>> {
return this.postPrivate('/asset/v1/private/transfer', params);
}
/** Exchange Coins */
getCoinExchangeHistory(
params?: UMExchangeCoinsRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/asset/v2/private/exchange/exchange-order-all',
params,
);
}
/** Get Borrow History */
getBorrowHistory(
params?: UMBorrowHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/unified/v3/private/account/borrow-history',
params,
);
}
/** Get Borrow Rate */
getBorrowRate(currency?: string): Promise<APIResponseV3<any>> {
return this.getPrivate('/unified/v3/private/account/borrow-rate', {
currency,
});
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
}

View File

@@ -1,337 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
USDCOptionsActiveOrdersRealtimeRequest,
USDCOptionsActiveOrdersRequest,
USDCOptionsCancelAllOrdersRequest,
USDCOptionsCancelOrderRequest,
USDCOptionsContractInfoRequest,
USDCOptionsDeliveryHistoryRequest,
USDCOptionsDeliveryPriceRequest,
USDCOptionsHistoricOrdersRequest,
USDCOptionsHistoricalVolatilityRequest,
USDCOptionsModifyMMPRequest,
USDCOptionsModifyOrderRequest,
USDCOptionsOrderExecutionRequest,
USDCOptionsOrderRequest,
USDCOptionsPositionsInfoExpiryRequest,
USDCOptionsRecentTradesRequest,
USDCPositionsRequest,
USDCTransactionLogRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for USDC Option APIs
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class USDCOptionClient extends BaseRestClient {
getClientType() {
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
/** Query order book info. Each side has a depth of 25 orders. */
getOrderBook(symbol: string): Promise<APIResponseV3<any>> {
return this.get('/option/usdc/openapi/public/v1/order-book', { symbol });
}
/** Fetch trading rules (such as min/max qty). Query for all if blank. */
getContractInfo(
params?: USDCOptionsContractInfoRequest,
): Promise<APIResponseV3<any>> {
return this.get('/option/usdc/openapi/public/v1/symbols', params);
}
/** Get a symbol price/statistics ticker */
getSymbolTicker(symbol: string): Promise<APIResponseV3<any>> {
return this.get('/option/usdc/openapi/public/v1/tick', { symbol });
}
/** Get delivery information */
getDeliveryPrice(
params?: USDCOptionsDeliveryPriceRequest,
): Promise<APIResponseV3<any>> {
return this.get('/option/usdc/openapi/public/v1/delivery-price', params);
}
/** Returned records are Taker Buy in default. */
getLast500Trades(
params: USDCOptionsRecentTradesRequest,
): Promise<APIResponseV3<any>> {
return this.get(
'/option/usdc/openapi/public/v1/query-trade-latest',
params,
);
}
/**
* The data is in hourly.
* If time field is not passed, it returns the recent 1 hour data by default.
* It could be any timeframe by inputting startTime & endTime, but it must satisfy [endTime - startTime] <= 30 days.
* It returns all data in 2 years when startTime & endTime are not passed.
* Both startTime & endTime entered together or both are left blank
*/
getHistoricalVolatility(
params?: USDCOptionsHistoricalVolatilityRequest,
): Promise<APIResponseV3<any>> {
return this.get(
'/option/usdc/openapi/public/v1/query-historical-volatility',
params,
);
}
/**
*
* Account Data Endpoints
*
*/
/** -> Order API */
/**
* Place an order using the USDC Derivatives Account.
* The request status can be queried in real-time.
* The response parameters must be queried through a query or a WebSocket response.
*/
submitOrder(params: USDCOptionsOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/place-order',
params,
);
}
/**
* Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request.
*/
batchSubmitOrders(
orderRequest: USDCOptionsOrderRequest[],
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/batch-place-orders',
{ orderRequest },
);
}
/** For Options, at least one of the three parameters — price, quantity or implied volatility — must be input. */
modifyOrder(
params: USDCOptionsModifyOrderRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/replace-order',
params,
);
}
/** Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request. */
batchModifyOrders(
replaceOrderRequest: USDCOptionsModifyOrderRequest[],
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/batch-replace-orders',
{ replaceOrderRequest },
);
}
/** Cancel order */
cancelOrder(
params: USDCOptionsCancelOrderRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/cancel-order',
params,
);
}
/** Batch cancel orders */
batchCancelOrders(
cancelRequest: USDCOptionsCancelOrderRequest[],
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/batch-cancel-orders',
{ cancelRequest },
);
}
/** This is used to cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */
cancelActiveOrders(
params?: USDCOptionsCancelAllOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/cancel-all',
params,
);
}
/** Query Unfilled/Partially Filled Orders(real-time), up to last 7 days of partially filled/unfilled orders */
getActiveRealtimeOrders(
params?: USDCOptionsActiveOrdersRealtimeRequest,
): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/option/usdc/openapi/private/v1/trade/query-active-orders',
params,
);
}
/** Query Unfilled/Partially Filled Orders */
getActiveOrders(
params: USDCOptionsActiveOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-active-orders',
params,
);
}
/** Query order history. The endpoint only supports up to 30 days of queried records */
getHistoricOrders(
params: USDCOptionsHistoricOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-order-history',
params,
);
}
/**
* Query trade history.
* The endpoint only supports up to 30 days of queried records.
* An error will be returned if startTime is more than 30 days.
*/
getOrderExecutionHistory(
params: USDCOptionsOrderExecutionRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/execution-list',
params,
);
}
/** -> Account API */
/** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
getTransactionLog(
params: USDCTransactionLogRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-transaction-log',
params,
);
}
/** Wallet info for USDC account. */
getBalances(): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-wallet-balance',
);
}
/** Asset Info */
getAssetInfo(baseCoin?: string): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-asset-info',
{ baseCoin },
);
}
/**
* If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN,
* and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN.
* X and Y will be adjusted according to operational requirements.
* Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode.
*/
setMarginMode(
newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN',
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/private/asset/account/setMarginMode',
{ setMarginMode: newMarginMode },
);
}
/** Query margin mode for USDC account. */
getMarginMode(): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-margin-info',
);
}
/** -> Positions API */
/** Query my positions */
getPositions(params: USDCPositionsRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-position',
params,
);
}
/** Query Delivery History */
getDeliveryHistory(
params: USDCOptionsDeliveryHistoryRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-delivery-list',
params,
);
}
/** Query Positions Info Upon Expiry */
getPositionsInfoUponExpiry(
params?: USDCOptionsPositionsInfoExpiryRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-position-exp-date',
params,
);
}
/** -> Market Maker Protection */
/** modifyMMP */
modifyMMP(params: USDCOptionsModifyMMPRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/mmp-modify',
params,
);
}
/** resetMMP */
resetMMP(currency: string): Promise<APIResponseV3<any>> {
return this.postPrivate('/option/usdc/openapi/private/v1/mmp-reset', {
currency,
});
}
/** queryMMPState */
queryMMPState(baseCoin: string): Promise<APIResponseV3<any>> {
return this.postPrivate('/option/usdc/openapi/private/v1/get-mmp-state', {
baseCoin,
});
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
}

View File

@@ -1,320 +0,0 @@
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
APIResponseV3,
APIResponseWithTime,
SymbolLimitParam,
SymbolPeriodLimitParam,
USDCKlineRequest,
USDCLast500TradesRequest,
USDCOpenInterestRequest,
USDCOrderFilter,
USDCPerpActiveOrdersRequest,
USDCPerpCancelOrderRequest,
USDCPerpHistoricOrdersRequest,
USDCPerpModifyOrderRequest,
USDCPerpOrderRequest,
USDCPositionsRequest,
USDCSymbolDirectionLimit,
USDCSymbolDirectionLimitCursor,
USDCTransactionLogRequest,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
/**
* REST API client for USDC Perpetual APIs
*
* @deprecated WARNING: V1/V2 private endpoints (Rest API & Websocket Stream) for mainnet
* will be switched off gradually from 30 Oct 2023 UTC, so they are not promised a stability.
* Please note that you are at your own risk of using old endpoints going forward, and please move to V5 ASAP.
*/
export class USDCPerpetualClient extends BaseRestClient {
getClientType() {
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
return REST_CLIENT_TYPE_ENUM.v3;
}
async fetchServerTime(): Promise<number> {
const res = await this.getServerTime();
return Number(res.time_now);
}
/**
*
* Market Data Endpoints
*
*/
getOrderBook(symbol: string): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/order-book', { symbol });
}
/** Fetch trading rules (such as min/max qty). Query for all if blank. */
getContractInfo(
params?: USDCSymbolDirectionLimit,
): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/symbols', params);
}
/** Get a symbol price/statistics ticker */
getSymbolTicker(symbol: string): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/tick', { symbol });
}
getCandles(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/kline/list', params);
}
getMarkPrice(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
return this.get(
'/perpetual/usdc/openapi/public/v1/mark-price-kline',
params,
);
}
getIndexPrice(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
return this.get(
'/perpetual/usdc/openapi/public/v1/index-price-kline',
params,
);
}
getIndexPremium(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
return this.get(
'/perpetual/usdc/openapi/public/v1/premium-index-kline',
params,
);
}
getOpenInterest(
params: USDCOpenInterestRequest,
): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/open-interest', params);
}
getLargeOrders(
params: SymbolLimitParam<string>,
): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/big-deal', params);
}
getLongShortRatio(
params: SymbolPeriodLimitParam<string>,
): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/account-ratio', params);
}
getLast500Trades(
params: USDCLast500TradesRequest,
): Promise<APIResponseV3<any>> {
return this.get(
'/option/usdc/openapi/public/v1/query-trade-latest',
params,
);
}
/**
*
* Account Data Endpoints
*
*/
/** -> Order API */
/**
* Place an order using the USDC Derivatives Account.
* The request status can be queried in real-time.
* The response parameters must be queried through a query or a WebSocket response.
*/
submitOrder(params: USDCPerpOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/place-order',
params,
);
}
/**
* Active order parameters (such as quantity, price) and stop order parameters cannot be modified in one request at the same time.
* Please request modification separately.
*/
modifyOrder(params: USDCPerpModifyOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/replace-order',
params,
);
}
/** Cancel order */
cancelOrder(params: USDCPerpCancelOrderRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/cancel-order',
params,
);
}
/** Cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */
cancelActiveOrders(
symbol: string,
orderFilter: USDCOrderFilter,
): Promise<APIResponseV3<any>> {
return this.postPrivate('/perpetual/usdc/openapi/private/v1/cancel-all', {
symbol,
orderFilter,
});
}
/** Query Unfilled/Partially Filled Orders */
getActiveOrders(
params: USDCPerpActiveOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-active-orders',
params,
);
}
/** Query order history. The endpoint only supports up to 30 days of queried records */
getHistoricOrders(
params: USDCPerpHistoricOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-order-history',
params,
);
}
/** Query trade history. The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
getOrderExecutionHistory(
params: USDCPerpActiveOrdersRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/execution-list',
params,
);
}
/** -> Account API */
/** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
getTransactionLog(
params: USDCTransactionLogRequest,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-transaction-log',
params,
);
}
/** Wallet info for USDC account. */
getBalances(): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-wallet-balance',
);
}
/** Asset Info */
getAssetInfo(baseCoin?: string): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-asset-info',
{ baseCoin },
);
}
/**
* If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN,
* and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN.
* X and Y will be adjusted according to operational requirements.
* Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode.
*/
setMarginMode(
newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN',
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/private/asset/account/setMarginMode',
{ setMarginMode: newMarginMode },
);
}
/** Query margin mode for USDC account. */
getMarginMode(): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-margin-info',
);
}
/** -> Positions API */
/** Query my positions */
getPositions(params: USDCPositionsRequest): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/query-position',
params,
);
}
/** Only for REGULAR_MARGIN */
setLeverage(symbol: string, leverage: string): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/position/leverage/save',
{ symbol, leverage },
);
}
/** Query Settlement History */
getSettlementHistory(
params?: USDCSymbolDirectionLimitCursor,
): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/option/usdc/openapi/private/v1/session-settlement',
params,
);
}
/** -> Risk Limit API */
/** Query risk limit */
getRiskLimit(symbol: string): Promise<APIResponseV3<any>> {
return this.getPrivate(
'/perpetual/usdc/openapi/public/v1/risk-limit/list',
{
symbol,
},
);
}
/** Set risk limit */
setRiskLimit(symbol: string, riskId: number): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/position/set-risk-limit',
{ symbol, riskId },
);
}
/** -> Funding API */
/** Funding settlement occurs every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC. The current interval's fund fee settlement is based on the previous interval's fund rate. For example, at 16:00, the settlement is based on the fund rate generated at 8:00. The fund rate generated at 16:00 will be used at 0:00 the next day. */
getLastFundingRate(symbol: string): Promise<APIResponseV3<any>> {
return this.get('/perpetual/usdc/openapi/public/v1/prev-funding-rate', {
symbol,
});
}
/** Get predicted funding rate and my predicted funding fee */
getPredictedFundingRate(symbol: string): Promise<APIResponseV3<any>> {
return this.postPrivate(
'/perpetual/usdc/openapi/private/v1/predicted-funding',
{ symbol },
);
}
/**
*
* API Data Endpoints
*
*/
getServerTime(): Promise<APIResponseWithTime> {
return this.get('/v2/public/time');
}
}

View File

@@ -4,7 +4,6 @@ import https from 'https';
import {
APIID,
REST_CLIENT_TYPE_ENUM,
RestClientOptions,
RestClientType,
getRestBaseUrl,
@@ -51,8 +50,6 @@ interface SignedRequestContext {
timestamp?: number;
api_key?: string;
recv_window?: number;
// spot v1 is diff from the rest...
recvWindow?: number;
}
interface SignedRequest<T> {
@@ -72,7 +69,7 @@ interface UnsignedRequest<T> {
recvWindow?: number;
}
type SignMethod = 'v2auth' | 'v5auth';
type SignMethod = 'v5auth';
export default abstract class BaseRestClient {
private timeOffset: number | null = null;
@@ -164,10 +161,6 @@ export default abstract class BaseRestClient {
}
}
private isSpotV1Client() {
return this.clientType === REST_CLIENT_TYPE_ENUM.spot;
}
get(endpoint: string, params?: any) {
return this._call('GET', endpoint, params, true);
}
@@ -263,56 +256,34 @@ export default abstract class BaseRestClient {
};
}
// USDC endpoints, unified margin and a few others use a different way of authenticating requests (headers instead of params)
if (this.clientType === REST_CLIENT_TYPE_ENUM.v3) {
const signResult = await this.prepareSignParams(
method,
'v5auth',
params,
isPublicApi,
);
const headers: AxiosRequestConfig['headers'] = {
'X-BAPI-SIGN-TYPE': 2,
'X-BAPI-API-KEY': this.key,
'X-BAPI-TIMESTAMP': signResult.timestamp,
'X-BAPI-SIGN': signResult.sign,
'X-BAPI-RECV-WINDOW': signResult.recvWindow,
...options.headers,
};
if (method === 'GET') {
return {
...options,
headers,
params: signResult.originalParams,
};
}
return {
...options,
headers,
data: signResult.originalParams,
};
}
const signResult = await this.prepareSignParams(
method,
'v2auth',
'v5auth',
params,
isPublicApi,
);
if (method === 'GET' || this.isSpotV1Client()) {
const headers: AxiosRequestConfig['headers'] = {
'X-BAPI-SIGN-TYPE': 2,
'X-BAPI-API-KEY': this.key,
'X-BAPI-TIMESTAMP': signResult.timestamp,
'X-BAPI-SIGN': signResult.sign,
'X-BAPI-RECV-WINDOW': signResult.recvWindow,
...options.headers,
};
if (method === 'GET') {
return {
...options,
params: signResult.paramsWithSign,
headers,
params: signResult.originalParams,
};
}
return {
...options,
data: signResult.paramsWithSign,
headers,
data: signResult.originalParams,
};
}
@@ -469,44 +440,6 @@ export default abstract class BaseRestClient {
return res;
}
// spot/v2 derivatives
if (signMethod === 'v2auth') {
res.originalParams.api_key = key;
res.originalParams.timestamp = timestamp;
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
if (recvWindow) {
if (this.isSpotV1Client()) {
res.originalParams.recvWindow = recvWindow;
} else {
res.originalParams.recv_window = recvWindow;
}
}
const sortProperties = true;
const encodeValues = false;
res.serializedParams = serializeParams(
res.originalParams,
strictParamValidation,
sortProperties,
encodeValues,
);
res.sign = await this.signMessage(
res.serializedParams,
this.secret,
'hex',
'SHA-256',
);
// @ts-ignore
res.paramsWithSign = {
...res.originalParams,
sign: res.sign,
};
return res;
}
return res;
}

View File

@@ -23,6 +23,8 @@ import {
} from './websockets';
import { WsOperation } from '../types/websockets/ws-api';
type UseTheExceptionEventInstead = never;
interface WSClientEventMap<WsKey extends string> {
/** Connection opened. If this connection was previously opened and reconnected, expect the reconnected event instead */
open: (evt: { wsKey: WsKey; event: any }) => void;
@@ -38,8 +40,17 @@ interface WSClientEventMap<WsKey extends string> {
) => void;
/** Received data for topic */
update: (response: any & { wsKey: WsKey }) => void;
/** Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) */
error: (response: any & { wsKey: WsKey; isWSAPIResponse?: boolean }) => void;
/**
* See for more information: https://github.com/tiagosiebler/bybit-api/issues/413
* @deprecated Use the 'exception' event instead. The 'error' event had the unintended consequence of throwing an unhandled promise rejection.
*/
error: UseTheExceptionEventInstead;
/**
* Exception from ws client OR custom listeners (e.g. if you throw inside your event handler)
*/
exception: (
response: any & { wsKey: WsKey; isWSAPIResponse?: boolean },
) => void;
/** Confirmation that a connection successfully authenticated */
authenticated: (event: {
wsKey: WsKey;
@@ -48,12 +59,6 @@ interface WSClientEventMap<WsKey extends string> {
}) => void;
}
export interface EmittableEvent<TEvent = any> {
eventType: 'response' | 'update' | 'error' | 'authenticated';
event: TEvent;
isWSAPIResponse?: boolean;
}
// Type safety for on and emit handlers: https://stackoverflow.com/a/61609010/880837
export interface BaseWebsocketClient<
TWSKey extends string,
@@ -71,13 +76,11 @@ export interface BaseWebsocketClient<
): boolean;
}
// interface TopicsPendingSubscriptions {
// wsKey: string;
// failedTopicsSubscriptions: Set<string>;
// pendingTopicsSubscriptions: Set<string>;
// resolver: TopicsPendingSubscriptionsResolver;
// rejector: TopicsPendingSubscriptionsRejector;
// }
export interface EmittableEvent<TEvent = any> {
eventType: 'response' | 'update' | 'exception' | 'authenticated';
event: TEvent;
isWSAPIResponse?: boolean;
}
/**
* A midflight WS request event (e.g. subscribe to these topics).
@@ -160,16 +163,17 @@ export abstract class BaseWebsocketClient<
reconnectTimeout: 500,
recvWindow: 5000,
// Calls to subscribeV5() are wrapped in a promise, allowing you to await a subscription request.
// Note: due to internal complexity, it's only recommended if you connect before subscribing.
promiseSubscribeRequests: false,
// Automatically send an authentication op/request after a connection opens, for private connections.
authPrivateConnectionsOnConnect: true,
// Individual requests do not require a signature, so this is disabled.
authPrivateRequests: false,
...options,
};
// add default error handling so this doesn't crash node (if the user didn't set a handler)
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.on('error', () => {});
}
/**
@@ -306,6 +310,9 @@ export abstract class BaseWebsocketClient<
for (const requestKey in pendingSubReqs) {
const request = pendingSubReqs[requestKey];
this.logger.trace(
`clearTopicsPendingSubscriptions(${wsKey}, ${rejectAll}, ${rejectReason}, ${requestKey}): rejecting promise for: ${JSON.stringify(request?.requestData || {})}`,
);
request?.rejector(request.requestData, rejectReason);
}
}
@@ -339,10 +346,18 @@ export abstract class BaseWebsocketClient<
pendingSubscriptionRequest.requestData,
);
} else {
pendingSubscriptionRequest.rejector(
pendingSubscriptionRequest.requestData,
this.logger.trace(
`updatePendingTopicSubscriptionStatus.reject(${wsKey}, ${requestKey}, ${msg}, ${isTopicSubscriptionSuccessEvent}): `,
msg,
);
try {
pendingSubscriptionRequest.rejector(
pendingSubscriptionRequest.requestData,
msg,
);
} catch (e) {
console.error('Exception rejecting promise: ', e);
}
}
this.removeTopicPendingSubscription(wsKey, requestKey);
@@ -582,7 +597,7 @@ export abstract class BaseWebsocketClient<
if (!error.message) {
this.logger.error(`${context} due to unexpected error: `, error);
this.emit('response', { ...error, wsKey });
this.emit('error', { ...error, wsKey });
this.emit('exception', { ...error, wsKey });
return;
}
@@ -614,8 +629,10 @@ export abstract class BaseWebsocketClient<
break;
}
this.logger.error(`parseWsError(${context}, ${error}, ${wsKey}) `, error);
this.emit('response', { ...error, wsKey });
this.emit('error', { ...error, wsKey });
this.emit('exception', { ...error, wsKey });
}
/** Get a signature, build the auth request and send it */
@@ -845,9 +862,11 @@ export abstract class BaseWebsocketClient<
for (const midflightRequest of subscribeWsMessages) {
const wsMessage = midflightRequest.requestEvent;
promises.push(
this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest),
);
if (this.options.promiseSubscribeRequests) {
promises.push(
this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest),
);
}
this.logger.trace(
`Sending batch via message: "${JSON.stringify(wsMessage)}"`,
@@ -890,9 +909,11 @@ export abstract class BaseWebsocketClient<
for (const midflightRequest of subscribeWsMessages) {
const wsMessage = midflightRequest.requestEvent;
promises.push(
this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest),
);
if (this.options.promiseSubscribeRequests) {
promises.push(
this.upsertPendingTopicSubscribeRequests(wsKey, midflightRequest),
);
}
this.logger.trace(`Sending batch via message: "${wsMessage}"`);
this.tryWsSend(wsKey, JSON.stringify(wsMessage));
@@ -908,7 +929,11 @@ export abstract class BaseWebsocketClient<
/**
* Try sending a string event on a WS connection (identified by the WS Key)
*/
public tryWsSend(wsKey: TWSKey, wsMessage: string) {
public tryWsSend(
wsKey: TWSKey,
wsMessage: string,
throwExceptions?: boolean,
) {
try {
this.logger.trace('Sending upstream ws message: ', {
...WS_LOGGER_CATEGORY,
@@ -934,6 +959,9 @@ export abstract class BaseWebsocketClient<
wsKey,
exception: e,
});
if (throwExceptions) {
throw e;
}
}
}
@@ -988,6 +1016,7 @@ export abstract class BaseWebsocketClient<
} catch (e) {
this.logger.error(
'Exception trying to resolve "connectionInProgress" promise',
e,
);
}
@@ -1001,12 +1030,28 @@ export abstract class BaseWebsocketClient<
);
// Request sub to public topics, if any
this.requestSubscribeTopics(wsKey, publicReqs);
try {
await this.requestSubscribeTopics(wsKey, publicReqs);
} catch (e) {
this.logger.error(
`onWsOpen(): exception in public requestSubscribeTopics(${wsKey}): `,
publicReqs,
e,
);
}
// Request sub to private topics, if auth on connect isn't needed
// Else, this is automatic after authentication is successfully confirmed
if (!this.options.authPrivateConnectionsOnConnect) {
this.requestSubscribeTopics(wsKey, privateReqs);
try {
this.requestSubscribeTopics(wsKey, privateReqs);
} catch (e) {
this.logger.error(
`onWsOpen(): exception in private requestSubscribeTopics(${wsKey}: `,
privateReqs,
e,
);
}
}
// Some websockets require an auth packet to be sent after opening the connection
@@ -1041,6 +1086,7 @@ export abstract class BaseWebsocketClient<
} catch (e) {
this.logger.error(
'Exception trying to resolve "connectionInProgress" promise',
e,
);
}
@@ -1107,10 +1153,6 @@ export abstract class BaseWebsocketClient<
}
for (const emittable of emittableEvents) {
// if (emittable.event?.op) {
// console.log('emittable: ', emittable);
// }
if (this.isWsPong(emittable)) {
this.logger.trace('Received pong2', {
...WS_LOGGER_CATEGORY,
@@ -1136,7 +1178,22 @@ export abstract class BaseWebsocketClient<
continue;
}
this.emit(emittable.eventType, emittableFinalEvent);
// this.logger.trace(
// `onWsMessage().emit(${emittable.eventType})`,
// emittableFinalEvent,
// );
try {
this.emit(emittable.eventType, emittableFinalEvent);
} catch (e) {
this.logger.error(
`Exception in onWsMessage().emit(${emittable.eventType}) handler:`,
e,
);
}
// this.logger.trace(
// `onWsMessage().emit(${emittable.eventType}).done()`,
// emittableFinalEvent,
// );
}
return;
@@ -1173,6 +1230,9 @@ export abstract class BaseWebsocketClient<
if (
this.wsStore.getConnectionState(wsKey) !== WsConnectionStateEnum.CLOSING
) {
this.logger.trace(
`onWsClose(${wsKey}): rejecting all deferred promises...`,
);
// clean up any pending promises for this connection
this.getWsStore().rejectAllDeferredPromises(
wsKey,
@@ -1187,6 +1247,9 @@ export abstract class BaseWebsocketClient<
this.emit('reconnect', { wsKey, event });
} else {
// clean up any pending promises for this connection
this.logger.trace(
`onWsClose(${wsKey}): rejecting all deferred promises...`,
);
this.getWsStore().rejectAllDeferredPromises(wsKey, 'disconnected');
this.setWsState(wsKey, WsConnectionStateEnum.INITIAL);
this.emit('close', { wsKey, event });

View File

@@ -1,10 +1,5 @@
import { AxiosResponse } from 'axios';
import { APIRateLimit } from '../types';
import {
WebsocketSucceededTopicSubscriptionConfirmationEvent,
WebsocketTopicSubscriptionConfirmationEvent,
} from '../types/websockets/ws-confirmations';
import { WSAPIResponse, WS_API_Operations } from '../types/websockets/ws-api';
export interface RestClientOptions {
/** Your API key */
@@ -188,58 +183,13 @@ export function isWsPong(msg: any): boolean {
);
}
export function isTopicSubscriptionConfirmation(
msg: unknown,
): msg is WebsocketTopicSubscriptionConfirmationEvent {
if (typeof msg !== 'object') {
return false;
}
if (!msg) {
return false;
}
if (typeof msg['op'] !== 'string') {
return false;
}
if (msg['op'] !== 'subscribe') {
return false;
}
return true;
}
export function isWSAPIResponse(
msg: unknown,
): msg is Omit<WSAPIResponse, 'wsKey'> {
if (typeof msg !== 'object' || !msg) {
return false;
}
if (typeof msg['op'] !== 'string') {
return false;
}
return (WS_API_Operations as string[]).includes(msg['op']);
}
export function isTopicSubscriptionSuccess(
msg: unknown,
): msg is WebsocketSucceededTopicSubscriptionConfirmationEvent {
if (!isTopicSubscriptionConfirmation(msg)) return false;
return msg.success === true;
}
export const APIID = 'bybitapinode';
/**
* Used to switch how authentication/requests work under the hood (primarily for SPOT since it's different there)
*/
export const REST_CLIENT_TYPE_ENUM = {
accountAsset: 'accountAsset',
inverse: 'inverse',
inverseFutures: 'inverseFutures',
linear: 'linear',
spot: 'spot',
v3: 'v3',
v5: 'v5',
} as const;
export type RestClientType =

View File

@@ -2,6 +2,11 @@
* Use type guards to narrow down types with minimal efforts.
*/
import {
WebsocketSucceededTopicSubscriptionConfirmationEvent,
WebsocketTopicSubscriptionConfirmationEvent,
} from '../types';
import { WSAPIResponse, WS_API_Operations } from '../types/websockets/ws-api';
import {
WSAccountOrderEventV5,
WSExecutionEventV5,
@@ -92,3 +97,47 @@ export function isWsExecutionEventV5(
return event['topic'] === 'execution';
}
export function neverGuard(x: never, msg: string): Error {
return new Error(`Unhandled value exception "${x}", ${msg}`);
}
export function isWSAPIResponse(
msg: unknown,
): msg is Omit<WSAPIResponse, 'wsKey'> {
if (typeof msg !== 'object' || !msg) {
return false;
}
if (typeof msg['op'] !== 'string') {
return false;
}
return (WS_API_Operations as string[]).includes(msg['op']);
}
export function isTopicSubscriptionSuccess(
msg: unknown,
): msg is WebsocketSucceededTopicSubscriptionConfirmationEvent {
if (!isTopicSubscriptionConfirmation(msg)) return false;
return msg.success === true;
}
export function isTopicSubscriptionConfirmation(
msg: unknown,
): msg is WebsocketTopicSubscriptionConfirmationEvent {
if (typeof msg !== 'object') {
return false;
}
if (!msg) {
return false;
}
if (typeof msg['op'] !== 'string') {
return false;
}
if (msg['op'] !== 'subscribe') {
return false;
}
return true;
}

View File

@@ -1,4 +1,4 @@
import { neverGuard } from './websockets';
import { neverGuard } from './typeGuards';
function bufferToB64(buffer: ArrayBuffer): string {
let binary = '';

View File

@@ -205,6 +205,9 @@ export class WsStore<
const promise = this.getDeferredPromise(wsKey, promiseRef);
if (promise?.reject) {
this.logger.trace(
`rejectDeferredPromise(): rejecting ${wsKey}/${promiseRef}/${value}`,
);
promise.reject(value);
}

View File

@@ -4,33 +4,16 @@ import {
CategoryV5,
WebsocketClientOptions,
WsKey,
WsTopic,
} from '../../types';
import { DefaultLogger } from '../logger';
import { WSAPIRequest } from '../../types/websockets/ws-api';
import { neverGuard } from '../typeGuards';
export const WS_LOGGER_CATEGORY = { category: 'bybit-ws' };
export const WS_KEY_MAP = {
inverse: 'inverse',
linearPrivate: 'linearPrivate',
linearPublic: 'linearPublic',
spotPrivate: 'spotPrivate',
spotPublic: 'spotPublic',
spotV3Private: 'spotV3Private',
spotV3Public: 'spotV3Public',
usdcOptionPrivate: 'usdcOptionPrivate',
usdcOptionPublic: 'usdcOptionPublic',
usdcPerpPrivate: 'usdcPerpPrivate',
usdcPerpPublic: 'usdcPerpPublic',
unifiedPrivate: 'unifiedPrivate',
unifiedOptionPublic: 'unifiedOptionPublic',
unifiedPerpUSDTPublic: 'unifiedPerpUSDTPublic',
unifiedPerpUSDCPublic: 'unifiedPerpUSDCPublic',
contractUSDTPublic: 'contractUSDTPublic',
contractUSDTPrivate: 'contractUSDTPrivate',
contractInversePublic: 'contractInversePublic',
contractInversePrivate: 'contractInversePrivate',
v5SpotPublic: 'v5SpotPublic',
v5LinearPublic: 'v5LinearPublic',
v5InversePublic: 'v5InversePublic',
@@ -43,27 +26,11 @@ export const WS_KEY_MAP = {
} as const;
export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
WS_KEY_MAP.spotV3Private,
WS_KEY_MAP.usdcOptionPrivate,
WS_KEY_MAP.usdcPerpPrivate,
WS_KEY_MAP.unifiedPrivate,
WS_KEY_MAP.contractUSDTPrivate,
WS_KEY_MAP.contractInversePrivate,
WS_KEY_MAP.v5Private,
WS_KEY_MAP.v5PrivateTrade,
];
export const PUBLIC_WS_KEYS = [
WS_KEY_MAP.linearPublic,
WS_KEY_MAP.spotPublic,
WS_KEY_MAP.spotV3Public,
WS_KEY_MAP.usdcOptionPublic,
WS_KEY_MAP.usdcPerpPublic,
WS_KEY_MAP.unifiedOptionPublic,
WS_KEY_MAP.unifiedPerpUSDTPublic,
WS_KEY_MAP.unifiedPerpUSDCPublic,
WS_KEY_MAP.contractUSDTPublic,
WS_KEY_MAP.contractInversePublic,
WS_KEY_MAP.v5SpotPublic,
WS_KEY_MAP.v5LinearPublic,
WS_KEY_MAP.v5InversePublic,
@@ -156,8 +123,6 @@ type PublicPrivateNetwork = 'public' | 'private';
* For the v5 endpoints, the subscribe/unsubscribe call must specify the category the subscription should route to.
*/
type PublicOnlyWsKeys =
| 'unifiedPerpUSDT'
| 'unifiedPerpUSDC'
| 'v5SpotPublic'
| 'v5LinearPublic'
| 'v5InversePublic'
@@ -216,126 +181,6 @@ export const WS_BASE_URL_MAP: Record<
testnet: 'wss://stream-testnet.bybit.com/v5/public/option',
},
},
inverse: {
public: {
livenet: 'wss://stream.bybit.com/realtime',
testnet: 'wss://stream-testnet.bybit.com/realtime',
},
private: {
livenet: 'wss://stream.bybit.com/realtime',
testnet: 'wss://stream-testnet.bybit.com/realtime',
},
},
linear: {
public: {
livenet: 'wss://stream.bybit.com/realtime_public',
livenet2: 'wss://stream.bytick.com/realtime_public',
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
},
private: {
livenet: 'wss://stream.bybit.com/realtime_private',
livenet2: 'wss://stream.bytick.com/realtime_private',
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
},
},
spot: {
public: {
livenet: 'wss://stream.bybit.com/spot/quote/ws/v1',
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
},
private: {
livenet: 'wss://stream.bybit.com/spot/ws',
testnet: 'wss://stream-testnet.bybit.com/spot/ws',
},
},
spotv3: {
public: {
livenet: 'wss://stream.bybit.com/spot/public/v3',
testnet: 'wss://stream-testnet.bybit.com/spot/public/v3',
},
private: {
livenet: 'wss://stream.bybit.com/spot/private/v3',
testnet: 'wss://stream-testnet.bybit.com/spot/private/v3',
},
},
usdcOption: {
public: {
livenet: 'wss://stream.bybit.com/trade/option/usdc/public/v1',
livenet2: 'wss://stream.bytick.com/trade/option/usdc/public/v1',
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/public/v1',
},
private: {
livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1',
livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1',
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1',
},
},
usdcPerp: {
public: {
livenet: 'wss://stream.bybit.com/perpetual/ws/v1/realtime_public',
livenet2: 'wss://stream.bytick.com/perpetual/ws/v1/realtime_public',
testnet: 'wss://stream-testnet.bybit.com/perpetual/ws/v1/realtime_public',
},
private: {
livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1',
livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1',
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1',
},
},
unifiedOption: {
public: {
livenet: 'wss://stream.bybit.com/option/usdc/public/v3',
testnet: 'wss://stream-testnet.bybit.com/option/usdc/public/v3',
},
private: {
livenet: 'wss://stream.bybit.com/unified/private/v3',
testnet: 'wss://stream-testnet.bybit.com/unified/private/v3',
},
},
unifiedPerp: {
public: {
livenet: 'useBaseSpecificEndpoint',
testnet: 'useBaseSpecificEndpoint',
},
private: {
livenet: 'wss://stream.bybit.com/unified/private/v3',
testnet: 'wss://stream-testnet.bybit.com/unified/private/v3',
},
},
unifiedPerpUSDT: {
public: {
livenet: 'wss://stream.bybit.com/contract/usdt/public/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3',
},
},
unifiedPerpUSDC: {
public: {
livenet: 'wss://stream.bybit.com/contract/usdc/public/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/usdc/public/v3',
},
},
contractUSDT: {
public: {
livenet: 'wss://stream.bybit.com/contract/usdt/public/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3',
},
private: {
livenet: 'wss://stream.bybit.com/contract/private/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/private/v3',
},
},
contractInverse: {
public: {
livenet: 'wss://stream.bybit.com/contract/inverse/public/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/inverse/public/v3',
},
private: {
livenet: 'wss://stream.bybit.com/contract/private/v3',
testnet: 'wss://stream-testnet.bybit.com/contract/private/v3',
},
},
};
export function isPrivateWsTopic(topic: string): boolean {
@@ -350,68 +195,6 @@ export function getWsKeyForTopic(
): WsKey {
const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic);
switch (market) {
case 'inverse': {
return WS_KEY_MAP.inverse;
}
case 'linear': {
return isPrivateTopic
? WS_KEY_MAP.linearPrivate
: WS_KEY_MAP.linearPublic;
}
case 'spot': {
return isPrivateTopic ? WS_KEY_MAP.spotPrivate : WS_KEY_MAP.spotPublic;
}
case 'spotv3': {
return isPrivateTopic
? WS_KEY_MAP.spotV3Private
: WS_KEY_MAP.spotV3Public;
}
case 'usdcOption': {
return isPrivateTopic
? WS_KEY_MAP.usdcOptionPrivate
: WS_KEY_MAP.usdcOptionPublic;
}
case 'usdcPerp': {
return isPrivateTopic
? WS_KEY_MAP.usdcPerpPrivate
: WS_KEY_MAP.usdcPerpPublic;
}
case 'unifiedOption': {
return isPrivateTopic
? WS_KEY_MAP.unifiedPrivate
: WS_KEY_MAP.unifiedOptionPublic;
}
case 'unifiedPerp': {
if (isPrivateTopic) {
return WS_KEY_MAP.unifiedPrivate;
}
const upperTopic = topic.toUpperCase();
if (upperTopic.indexOf('USDT') !== -1) {
return WS_KEY_MAP.unifiedPerpUSDTPublic;
}
if (
upperTopic.indexOf('USDC') !== -1 ||
upperTopic.indexOf('PERP') !== -1
) {
return WS_KEY_MAP.unifiedPerpUSDCPublic;
}
throw new Error(
`Failed to determine wskey for unified perps topic: "${topic}"`,
);
}
case 'contractInverse': {
return isPrivateTopic
? WS_KEY_MAP.contractInversePrivate
: WS_KEY_MAP.contractInversePublic;
}
case 'contractUSDT': {
return isPrivateTopic
? WS_KEY_MAP.contractUSDTPrivate
: WS_KEY_MAP.contractUSDTPublic;
}
case 'v5': {
if (isPrivateTopic) {
return WS_KEY_MAP.v5Private;
@@ -458,19 +241,23 @@ export function getWsUrl(
}
// https://bybit-exchange.github.io/docs/v5/demo
const isDemoTrading = wsClientOptions.demoTrading;
if (isDemoTrading) {
return 'wss://stream-demo.bybit.com/v5/private';
}
const DEMO_TRADING_ENDPOINT = 'wss://stream-demo.bybit.com/v5/private';
const isDemoTrading = wsClientOptions.demoTrading;
const isTestnet = wsClientOptions.testnet;
const networkKey = isTestnet ? 'testnet' : 'livenet';
switch (wsKey) {
case WS_KEY_MAP.v5Private: {
if (isDemoTrading) {
return DEMO_TRADING_ENDPOINT;
}
return WS_BASE_URL_MAP.v5.private[networkKey];
}
case WS_KEY_MAP.v5PrivateTrade: {
if (isDemoTrading) {
return DEMO_TRADING_ENDPOINT;
}
return WS_BASE_URL_MAP[wsKey].private[networkKey];
}
case WS_KEY_MAP.v5SpotPublic: {
@@ -485,64 +272,6 @@ export function getWsUrl(
case WS_KEY_MAP.v5OptionPublic: {
return WS_BASE_URL_MAP.v5OptionPublic.public[networkKey];
}
case WS_KEY_MAP.linearPublic: {
return WS_BASE_URL_MAP.linear.public[networkKey];
}
case WS_KEY_MAP.linearPrivate: {
return WS_BASE_URL_MAP.linear.private[networkKey];
}
case WS_KEY_MAP.spotPublic: {
return WS_BASE_URL_MAP.spot.public[networkKey];
}
case WS_KEY_MAP.spotPrivate: {
return WS_BASE_URL_MAP.spot.private[networkKey];
}
case WS_KEY_MAP.spotV3Public: {
return WS_BASE_URL_MAP.spotv3.public[networkKey];
}
case WS_KEY_MAP.spotV3Private: {
return WS_BASE_URL_MAP.spotv3.private[networkKey];
}
case WS_KEY_MAP.inverse: {
// private and public are on the same WS connection
return WS_BASE_URL_MAP.inverse.public[networkKey];
}
case WS_KEY_MAP.usdcOptionPublic: {
return WS_BASE_URL_MAP.usdcOption.public[networkKey];
}
case WS_KEY_MAP.usdcOptionPrivate: {
return WS_BASE_URL_MAP.usdcOption.private[networkKey];
}
case WS_KEY_MAP.usdcPerpPublic: {
return WS_BASE_URL_MAP.usdcPerp.public[networkKey];
}
case WS_KEY_MAP.usdcPerpPrivate: {
return WS_BASE_URL_MAP.usdcPerp.private[networkKey];
}
case WS_KEY_MAP.unifiedOptionPublic: {
return WS_BASE_URL_MAP.unifiedOption.public[networkKey];
}
case WS_KEY_MAP.unifiedPerpUSDTPublic: {
return WS_BASE_URL_MAP.unifiedPerpUSDT.public[networkKey];
}
case WS_KEY_MAP.unifiedPerpUSDCPublic: {
return WS_BASE_URL_MAP.unifiedPerpUSDC.public[networkKey];
}
case WS_KEY_MAP.unifiedPrivate: {
return WS_BASE_URL_MAP.unifiedPerp.private[networkKey];
}
case WS_KEY_MAP.contractInversePrivate: {
return WS_BASE_URL_MAP.contractInverse.private[networkKey];
}
case WS_KEY_MAP.contractInversePublic: {
return WS_BASE_URL_MAP.contractInverse.public[networkKey];
}
case WS_KEY_MAP.contractUSDTPrivate: {
return WS_BASE_URL_MAP.contractUSDT.private[networkKey];
}
case WS_KEY_MAP.contractUSDTPublic: {
return WS_BASE_URL_MAP.contractUSDT.public[networkKey];
}
default: {
logger.error('getWsUrl(): Unhandled wsKey: ', {
category: 'bybit-ws',
@@ -557,50 +286,19 @@ export function getMaxTopicsPerSubscribeEvent(
market: APIMarket,
wsKey: WsKey,
): number | null {
const topicsPerEventSpot = 10;
switch (market) {
case 'inverse':
case 'linear':
case 'usdcOption':
case 'usdcPerp':
case 'unifiedOption':
case 'unifiedPerp':
case 'spot':
case 'contractInverse':
case 'contractUSDT':
case 'v5': {
if (wsKey === WS_KEY_MAP.v5SpotPublic) {
return topicsPerEventSpot;
return 10;
}
return null;
}
case 'spotv3': {
return topicsPerEventSpot;
}
default: {
throw neverGuard(market, 'getWsKeyForTopic(): Unhandled market');
}
}
}
export function getUsdcWsKeyForTopic(
topic: string,
subGroup: 'option' | 'perp',
): WsKey {
const isPrivateTopic = PRIVATE_TOPICS.includes(topic);
if (subGroup === 'option') {
return isPrivateTopic
? WS_KEY_MAP.usdcOptionPrivate
: WS_KEY_MAP.usdcOptionPublic;
}
return isPrivateTopic
? WS_KEY_MAP.usdcOptionPrivate
: WS_KEY_MAP.usdcOptionPublic;
// return isPrivateTopic
// ? WS_KEY_MAP.usdcPerpPrivate
// : WS_KEY_MAP.usdcPerpPublic;
}
export const WS_ERROR_ENUM = {
NOT_AUTHENTICATED_SPOT_V3: '-1004',
API_ERROR_GENERIC: '10001',
@@ -608,10 +306,6 @@ export const WS_ERROR_ENUM = {
USDC_OPTION_AUTH_FAILED: '3303006',
};
export function neverGuard(x: never, msg: string): Error {
return new Error(`Unhandled value exception "x", ${msg}`);
}
/**
* #305: ws.terminate() is undefined in browsers.
* This only works in node.js, not in browsers.
@@ -660,3 +354,44 @@ export function getNormalisedTopicRequests(
}
return normalisedTopicRequests;
}
/**
* Groups topics in request into per-wsKey groups
* @param normalisedTopicRequests
* @param wsKey
* @param isPrivateTopic
* @returns
*/
export function getTopicsPerWSKey(
market: APIMarket,
normalisedTopicRequests: WsTopicRequest[],
wsKey?: WsKey,
isPrivateTopic?: boolean,
): {
[key in WsKey]?: WsTopicRequest<WsTopic>[];
} {
const perWsKeyTopics: { [key in WsKey]?: WsTopicRequest<WsTopic>[] } = {};
// Sort into per wsKey arrays, in case topics are mixed together for different wsKeys
for (const topicRequest of normalisedTopicRequests) {
const derivedWsKey =
wsKey ||
getWsKeyForTopic(
market,
topicRequest.topic,
isPrivateTopic,
topicRequest.category,
);
if (
!perWsKeyTopics[derivedWsKey] ||
!Array.isArray(perWsKeyTopics[derivedWsKey])
) {
perWsKeyTopics[derivedWsKey] = [];
}
perWsKeyTopics[derivedWsKey]!.push(topicRequest);
}
return perWsKeyTopics;
}

View File

@@ -1,6 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
/* eslint-disable max-len */
/* eslint-disable @typescript-eslint/no-explicit-any */
import WebSocket from 'isomorphic-ws';
import {
@@ -19,6 +16,7 @@ import {
getMaxTopicsPerSubscribeEvent,
getNormalisedTopicRequests,
getPromiseRefForWSAPIRequest,
getTopicsPerWSKey,
getWsKeyForTopic,
getWsUrl,
isPrivateWsTopic,
@@ -35,6 +33,7 @@ import {
} from './util/BaseWSClient';
import {
Exact,
WSAPIOperation,
WSAPIRequest,
WsAPIOperationResponseMap,
WsAPITopicRequestParamMap,
@@ -46,74 +45,18 @@ import { SignAlgorithm, signMessage } from './util/webCryptoAPI';
const WS_LOGGER_CATEGORY = { category: 'bybit-ws' };
/**
* Groups topics in request into per-wsKey groups
* @param normalisedTopicRequests
* @param wsKey
* @param isPrivateTopic
* @returns
*/
function getTopicsPerWSKey(
normalisedTopicRequests: WsTopicRequest[],
wsKey?: WsKey,
isPrivateTopic?: boolean,
): {
[key in WsKey]?: WsTopicRequest<WsTopic>[];
} {
const perWsKeyTopics: { [key in WsKey]?: WsTopicRequest<WsTopic>[] } = {};
// Sort into per wsKey arrays, in case topics are mixed together for different wsKeys
for (const topicRequest of normalisedTopicRequests) {
const derivedWsKey =
wsKey ||
getWsKeyForTopic(
this.options.market,
topicRequest.topic,
isPrivateTopic,
topicRequest.category,
);
if (
!perWsKeyTopics[derivedWsKey] ||
!Array.isArray(perWsKeyTopics[derivedWsKey])
) {
perWsKeyTopics[derivedWsKey] = [];
}
perWsKeyTopics[derivedWsKey]!.push(topicRequest);
}
return perWsKeyTopics;
}
// export class WebsocketClient extends EventEmitter {
export class WebsocketClient extends BaseWebsocketClient<
WsKey,
WsRequestOperationBybit<WsTopic>
> {
/**
* Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library
* Request connection of all dependent (public & private) websockets, instead of waiting
* for automatic connection by SDK.
*/
public connectAll(): Promise<WSConnectedResult | undefined>[] {
switch (this.options.market) {
case 'inverse': {
// only one for inverse
return [...this.connectPublic()];
}
// these all have separate public & private ws endpoints
case 'linear':
case 'spot':
case 'spotv3':
case 'usdcOption':
case 'usdcPerp':
case 'unifiedPerp':
case 'unifiedOption':
case 'contractUSDT':
case 'contractInverse': {
return [...this.connectPublic(), this.connectPrivate()];
}
case 'v5': {
return [this.connectPrivate()];
return [...this.connectPublic(), this.connectPrivate()];
}
default: {
throw neverGuard(this.options.market, 'connectAll(): Unhandled market');
@@ -143,37 +86,6 @@ export class WebsocketClient extends BaseWebsocketClient<
this.connect(WS_KEY_MAP.v5OptionPublic),
];
}
case 'inverse': {
return [this.connect(WS_KEY_MAP.inverse)];
}
case 'linear': {
return [this.connect(WS_KEY_MAP.linearPublic)];
}
case 'spot': {
return [this.connect(WS_KEY_MAP.spotPublic)];
}
case 'spotv3': {
return [this.connect(WS_KEY_MAP.spotV3Public)];
}
case 'usdcOption': {
return [this.connect(WS_KEY_MAP.usdcOptionPublic)];
}
case 'usdcPerp': {
return [this.connect(WS_KEY_MAP.usdcPerpPublic)];
}
case 'unifiedOption': {
return [this.connect(WS_KEY_MAP.unifiedOptionPublic)];
}
case 'unifiedPerp': {
return [
this.connect(WS_KEY_MAP.unifiedPerpUSDTPublic),
this.connect(WS_KEY_MAP.unifiedPerpUSDCPublic),
];
}
case 'contractUSDT':
return [this.connect(WS_KEY_MAP.contractUSDTPublic)];
case 'contractInverse':
return [this.connect(WS_KEY_MAP.contractInversePublic)];
}
}
@@ -183,41 +95,16 @@ export class WebsocketClient extends BaseWebsocketClient<
default: {
return this.connect(WS_KEY_MAP.v5Private);
}
case 'inverse': {
return this.connect(WS_KEY_MAP.inverse);
}
case 'linear': {
return this.connect(WS_KEY_MAP.linearPrivate);
}
case 'spot': {
return this.connect(WS_KEY_MAP.spotPrivate);
}
case 'spotv3': {
return this.connect(WS_KEY_MAP.spotV3Private);
}
case 'usdcOption': {
return this.connect(WS_KEY_MAP.usdcOptionPrivate);
}
case 'usdcPerp': {
return this.connect(WS_KEY_MAP.usdcPerpPrivate);
}
case 'unifiedPerp':
case 'unifiedOption': {
return this.connect(WS_KEY_MAP.unifiedPrivate);
}
case 'contractUSDT':
return this.connect(WS_KEY_MAP.contractUSDTPrivate);
case 'contractInverse':
return this.connect(WS_KEY_MAP.contractInversePrivate);
}
}
/**
*
* Subscribe to V5 topics & track/persist them.
* @param wsTopics - topic or list of topics
* @param category - the API category this topic is for (e.g. "linear"). The value is only important when connecting to public topics and will be ignored for private topics.
* @param isPrivateTopic - optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet)
* @param category - the API category this topic is for (e.g. "linear").
* The value is only important when connecting to public topics and will be ignored for private topics.
* @param isPrivateTopic - optional - the library will try to detect private topics, you can use this
* to mark a topic as private (if the topic isn't recognised yet)
*/
public subscribeV5(
wsTopics: WsTopic[] | WsTopic,
@@ -249,7 +136,7 @@ export class WebsocketClient extends BaseWebsocketClient<
perWsKeyTopics[derivedWsKey] = [];
}
perWsKeyTopics[derivedWsKey].push(wsRequest);
perWsKeyTopics[derivedWsKey]!.push(wsRequest);
}
const promises: Promise<unknown>[] = [];
@@ -276,10 +163,14 @@ export class WebsocketClient extends BaseWebsocketClient<
}
/**
* Unsubscribe from V5 topics & remove them from memory. They won't be re-subscribed to if the connection reconnects.
* Unsubscribe from V5 topics & remove them from memory. They won't be re-subscribed to if the
* connection reconnects.
*
* @param wsTopics - topic or list of topics
* @param category - the API category this topic is for (e.g. "linear"). The value is only important when connecting to public topics and will be ignored for private topics.
* @param isPrivateTopic - optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet)
* @param category - the API category this topic is for (e.g. "linear"). The value is only
* important when connecting to public topics and will be ignored for private topics.
* @param isPrivateTopic - optional - the library will try to detect private topics, you can
* use this to mark a topic as private (if the topic isn't recognised yet)
*/
public unsubscribeV5(
wsTopics: WsTopic[] | WsTopic,
@@ -338,7 +229,11 @@ export class WebsocketClient extends BaseWebsocketClient<
}
/**
* Request subscription to one or more topics. Pass topics as either an array of strings, or array of objects (if the topic has parameters).
* Note: subscribeV5() might be simpler to use. The end result is the same.
*
* Request subscription to one or more topics. Pass topics as either an array of strings,
* or array of objects (if the topic has parameters).
*
* Objects should be formatted as {topic: string, params: object, category: CategoryV5}.
*
* - Subscriptions are automatically routed to the correct websocket connection.
@@ -356,7 +251,11 @@ export class WebsocketClient extends BaseWebsocketClient<
const topicRequests = Array.isArray(requests) ? requests : [requests];
const normalisedTopicRequests = getNormalisedTopicRequests(topicRequests);
const perWsKeyTopics = getTopicsPerWSKey(normalisedTopicRequests, wsKey);
const perWsKeyTopics = getTopicsPerWSKey(
this.options.market,
normalisedTopicRequests,
wsKey,
);
// Batch sub topics per ws key
for (const wsKey in perWsKeyTopics) {
@@ -368,6 +267,7 @@ export class WebsocketClient extends BaseWebsocketClient<
}
/**
* Note: unsubscribe() might be simpler to use. The end result is the same.
* Unsubscribe from one or more topics. Similar to subscribe() but in reverse.
*
* - Requests are automatically routed to the correct websocket connection.
@@ -382,7 +282,11 @@ export class WebsocketClient extends BaseWebsocketClient<
const topicRequests = Array.isArray(requests) ? requests : [requests];
const normalisedTopicRequests = getNormalisedTopicRequests(topicRequests);
const perWsKeyTopics = getTopicsPerWSKey(normalisedTopicRequests, wsKey);
const perWsKeyTopics = getTopicsPerWSKey(
this.options.market,
normalisedTopicRequests,
wsKey,
);
// Batch sub topics per ws key
for (const wsKey in perWsKeyTopics) {
@@ -393,108 +297,124 @@ export class WebsocketClient extends BaseWebsocketClient<
}
}
/*******
*
*
*
*
* OLD WS CLIENT BELOW
/**
*
*
*
* WS API Methods - similar to the REST API, but via WebSockets
* https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline
*
*
*
*/
/**
* Subscribe to V1-V3 topics & track/persist them.
* Send a Websocket API command/request on a connection. Returns a promise that resolves on reply.
*
* @deprecated The V1-V3 websockets are very old and may not work properly anymore. Support for them will be removed soon. Use subcribeV5/unsubscribeV5 or subscribe/unsubscribe instead.
* WS API Documentation for list of operations and parameters:
* https://bybit-exchange.github.io/docs/v5/websocket/trade/guideline
*
* Note: for public V5 topics use the `subscribeV5()` method.
* Returned promise is rejected if:
* - an exception is detected in the reply, OR
* - the connection disconnects for any reason (even if automatic reconnect will happen).
*
* Topics will be automatically resubscribed to if the connection resets/drops/reconnects.
* @param wsTopics - topic or list of topics
* @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet)
* Authentication is automatic. If you didn't request authentication yourself, there might
* be a small delay after your first request, while the SDK automatically authenticates.
*
* @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported
* for Bybit, since that is the dedicated WS API connection.
* @param operation - The command being sent, e.g. "order.create" to submit a new order.
* @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new
* order. Only send parameters for the request body. Everything else is automatically handled.
* @returns Promise - tries to resolve with async WS API response. Rejects if disconnected or exception is seen in async WS API response
*/
public subscribeV3(
wsTopics: WsTopic[] | WsTopic,
isPrivateTopic?: boolean,
): Promise<unknown>[] {
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
if (this.options.market === 'v5') {
topics.forEach((topic) => {
if (!isPrivateWsTopic(topic)) {
throw new Error(
'For public "v5" websocket topics, use the subscribeV5() method & provide the category parameter',
);
}
});
}
const promises: Promise<unknown>[] = [];
// This overload allows the caller to omit the 3rd param, if it isn't required
sendWSAPIRequest<
TWSKey extends keyof WsAPIWsKeyTopicMap,
TWSOperation extends WsAPIWsKeyTopicMap[TWSKey],
TWSParams extends Exact<WsAPITopicRequestParamMap[TWSOperation]>,
>(
wsKey: TWSKey,
operation: TWSOperation,
...params: TWSParams extends undefined ? [] : [TWSParams]
): Promise<WsAPIOperationResponseMap[TWSOperation]>;
topics.forEach((topic) => {
const wsKey = getWsKeyForTopic(
this.options.market,
topic,
isPrivateTopic,
// These overloads give stricter types than mapped generics, since generic constraints
// do not trigger excess property checks
// Without these overloads, TypeScript won't complain if you include an
// unexpected property with your request (if it doesn't clash with an existing property)
sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.create'>(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: TWSOpreation,
params: WsAPITopicRequestParamMap[TWSOpreation],
): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.amend'>(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: TWSOpreation,
params: WsAPITopicRequestParamMap[TWSOpreation],
): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
sendWSAPIRequest<TWSOpreation extends WSAPIOperation = 'order.cancel'>(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: TWSOpreation,
params: WsAPITopicRequestParamMap[TWSOpreation],
): Promise<WsAPIOperationResponseMap[TWSOpreation]>;
async sendWSAPIRequest<
TWSKey extends keyof WsAPIWsKeyTopicMap,
TWSOperation extends WsAPIWsKeyTopicMap[TWSKey],
TWSParams extends Exact<WsAPITopicRequestParamMap[TWSOperation]>,
TWSAPIResponse extends
WsAPIOperationResponseMap[TWSOperation] = WsAPIOperationResponseMap[TWSOperation],
>(
wsKey: WsKey = WS_KEY_MAP.v5PrivateTrade,
operation: TWSOperation,
params: TWSParams,
): Promise<WsAPIOperationResponseMap[TWSOperation]> {
this.logger.trace(`sendWSAPIRequest(): assert "${wsKey}" is connected`);
await this.assertIsConnected(wsKey);
this.logger.trace('sendWSAPIRequest()->assertIsConnected() ok');
await this.assertIsAuthenticated(wsKey);
this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok');
const requestEvent: WSAPIRequest<TWSParams> = {
reqId: this.getNewRequestId(),
header: {
'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`,
'X-BAPI-TIMESTAMP': `${Date.now()}`,
Referer: APIID,
},
op: operation,
args: [params],
};
// Sign, if needed
const signedEvent = await this.signWSAPIRequest(requestEvent);
// Store deferred promise, resolved within the "resolveEmittableEvents" method while parsing incoming events
const promiseRef = getPromiseRefForWSAPIRequest(requestEvent);
const deferredPromise =
this.getWsStore().createDeferredPromise<TWSAPIResponse>(
wsKey,
promiseRef,
false,
);
const wsRequest: WsTopicRequest<WsTopic> = {
topic: topic,
};
this.logger.trace(
`sendWSAPIRequest(): sending raw request: ${JSON.stringify(signedEvent, null, 2)}`,
);
// Persist topic for reconnects
const requestPromise = this.subscribeTopicsForWsKey([wsRequest], wsKey);
// Send event
this.tryWsSend(wsKey, JSON.stringify(signedEvent));
promises.push(requestPromise);
});
this.logger.trace(`sendWSAPIRequest(): sent ${operation} event`);
// Return promise to resolve midflight WS request (only works if already connected before request)
return promises;
}
/**
* Unsubscribe from V1-V3 topics & remove them from memory. They won't be re-subscribed to if the connection reconnects.
*
* @deprecated The V1-V3 websockets are very old and may not work properly anymore. Support for them will be removed soon. Use subcribeV5/unsubscribeV5 or subscribe/unsubscribe instead.
*
* Note: For public V5 topics, use `unsubscribeV5()` instead!
*
* @param wsTopics topic or list of topics
* @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet)
*/
public unsubscribeV3(
wsTopics: WsTopic[] | WsTopic,
isPrivateTopic?: boolean,
) {
const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics];
if (this.options.market === 'v5') {
topics.forEach((topic) => {
if (!isPrivateWsTopic(topic)) {
throw new Error(
'For public "v5" websocket topics, use the unsubscribeV5() method & provide the category parameter',
);
}
});
}
topics.forEach((topic) => {
const wsKey = getWsKeyForTopic(
this.options.market,
topic,
isPrivateTopic,
);
const wsRequest: WsTopicRequest<WsTopic> = {
topic: topic,
};
// Persist topic for reconnects
this.unsubscribeTopicsForWsKey([wsRequest], wsKey);
});
// Return deferred promise, so caller can await this call
return deferredPromise.promise!;
}
/**
@@ -512,7 +432,7 @@ export class WebsocketClient extends BaseWebsocketClient<
const wsBaseURL = getWsUrl(wsKey, this.options, this.logger);
// If auth is needed for this wsKey URL, this returns a suffix
const authParams = await this.getWsAuthURLSuffix(wsKey);
const authParams = await this.getWsAuthURLSuffix();
if (!authParams) {
return wsBaseURL;
}
@@ -523,8 +443,7 @@ export class WebsocketClient extends BaseWebsocketClient<
/**
* Return params required to make authorized request
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
private async getWsAuthURLSuffix(wsKey: WsKey): Promise<string> {
private async getWsAuthURLSuffix(): Promise<string> {
return '';
}
@@ -540,7 +459,9 @@ export class WebsocketClient extends BaseWebsocketClient<
return await signMessage(paramsStr, secret, method, algorithm);
}
protected async getWsAuthRequestEvent(wsKey: WsKey): Promise<any> {
protected async getWsAuthRequestEvent(
wsKey: WsKey,
): Promise<WsRequestOperationBybit<string>> {
try {
const { signature, expiresAt } = await this.getWsAuthSignature(wsKey);
@@ -608,7 +529,6 @@ export class WebsocketClient extends BaseWebsocketClient<
}
/** Force subscription requests to be sent in smaller batches, if a number is returned */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null {
return getMaxTopicsPerSubscribeEvent(this.options.market, wsKey);
}
@@ -630,10 +550,18 @@ export class WebsocketClient extends BaseWebsocketClient<
switch (market) {
case 'all': {
const topics = requests.map((r) => r.topic);
// Previously used to track topics in a request. Keeping this for subscribe/unsubscribe requests, no need for incremental values
const req_id =
['subscribe', 'unsubscribe'].includes(operation) && topics.length
? topics.join(',')
: this.getNewRequestId();
const wsEvent: WsRequestOperationBybit<WsTopic> = {
req_id: this.getNewRequestId(),
req_id: req_id,
op: operation,
args: requests.map((r) => r.topic),
args: topics,
};
const midflightWsEvent: MidflightWsRequestEvent<
@@ -656,6 +584,7 @@ export class WebsocketClient extends BaseWebsocketClient<
if (wsRequestBuildingErrors.length) {
const label =
wsRequestBuildingErrors.length === requests.length ? 'all' : 'some';
this.logger.error(
`Failed to build/send ${wsRequestBuildingErrors.length} event(s) for ${label} WS requests due to exceptions`,
{
@@ -674,17 +603,7 @@ export class WebsocketClient extends BaseWebsocketClient<
}
protected getPrivateWSKeys(): WsKey[] {
return [
WS_KEY_MAP.linearPrivate,
WS_KEY_MAP.spotPrivate,
WS_KEY_MAP.spotV3Private,
WS_KEY_MAP.usdcOptionPrivate,
WS_KEY_MAP.usdcPerpPrivate,
WS_KEY_MAP.unifiedPrivate,
WS_KEY_MAP.contractUSDTPrivate,
WS_KEY_MAP.contractInversePrivate,
WS_KEY_MAP.v5Private,
];
return WS_AUTH_ON_CONNECT_KEYS;
}
protected isAuthOnConnectWsKey(wsKey: WsKey): boolean {
@@ -694,11 +613,7 @@ export class WebsocketClient extends BaseWebsocketClient<
/**
* Determines if a topic is for a private channel, using a hardcoded list of strings
*/
protected isPrivateTopicRequest(
request: WsTopicRequest<string>,
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
wsKey: WsKey,
): boolean {
protected isPrivateTopicRequest(request: WsTopicRequest<string>): boolean {
const topicName = request?.topic?.toLowerCase();
if (!topicName) {
return false;
@@ -707,6 +622,7 @@ export class WebsocketClient extends BaseWebsocketClient<
return isPrivateWsTopic(topicName);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected isWsPing(msg: any): boolean {
if (!msg) {
return false;
@@ -726,6 +642,7 @@ export class WebsocketClient extends BaseWebsocketClient<
return false;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected isWsPong(msg: any): boolean {
if (!msg) {
return false;
@@ -763,7 +680,6 @@ export class WebsocketClient extends BaseWebsocketClient<
event: MessageEventLike,
): EmittableEvent[] {
const results: EmittableEvent[] = [];
// const isWSAPIResponseEvent = wsKey === WS_KEY_MAP.v5PrivateTrade;
try {
const parsed = JSON.parse(event.data);
@@ -839,7 +755,7 @@ export class WebsocketClient extends BaseWebsocketClient<
}
results.push({
eventType: 'error',
eventType: 'exception',
event: parsed,
isWSAPIResponse: true,
});
@@ -879,7 +795,6 @@ export class WebsocketClient extends BaseWebsocketClient<
results.push({
eventType: 'update',
event: parsed,
// isWSAPIResponse: isWSAPIResponseEvent,
});
return results;
}
@@ -889,9 +804,8 @@ export class WebsocketClient extends BaseWebsocketClient<
// Failed request
if (parsed.success === false) {
results.push({
eventType: 'error',
eventType: 'exception',
event: parsed,
// isWSAPIResponse: isWSAPIResponseEvent,
});
return results;
}
@@ -901,7 +815,6 @@ export class WebsocketClient extends BaseWebsocketClient<
results.push({
eventType: 'response',
event: parsed,
// isWSAPIResponse: isWSAPIResponseEvent,
});
return results;
}
@@ -911,7 +824,6 @@ export class WebsocketClient extends BaseWebsocketClient<
results.push({
eventType: 'authenticated',
event: parsed,
// isWSAPIResponse: isWSAPIResponseEvent,
});
return results;
}
@@ -939,7 +851,7 @@ export class WebsocketClient extends BaseWebsocketClient<
exception: e,
eventData: event.data,
},
eventType: 'error',
eventType: 'exception',
});
this.logger.error('Failed to parse event data due to exception: ', {
@@ -950,121 +862,4 @@ export class WebsocketClient extends BaseWebsocketClient<
return results;
}
/**
*
*
*
* WS API Methods - similar to the REST API, but via WebSockets
*
*
*
*/
/**
* Send a Websocket API event on a connection. Returns a promise that resolves on reply.
*
* Authentication is automatic. If you didn't request authentication yourself, there might be a small delay after your first request, while the SDK automatically authenticates.
*
* Returned promise is rejected if:
* - an exception is detected in the reply, OR
* - the connection disconnects for any reason (even if automatic reconnect will happen).
*
* If you authenticated once and you're reconnected later (e.g. connection temporarily lost), the SDK will by default automatically:
* - Detect you were authenticated to the WS API before
* - Try to re-authenticate (up to 5 times, in case something (bad timestamp) goes wrong)
* - If it succeeds, it will emit the 'authenticated' event.
* - If it fails and gives up, it will emit an 'exception' event.
*
* @param wsKey - The connection this event is for. Currently only "v5PrivateTrade" is supported, since that is the dedicated WS API connection.
* @param operation - The command being sent, e.g. "order.create" to submit a new order.
* @param params - Any request parameters for the command. E.g. `OrderParamsV5` to submit a new order. Only send parameters for the request body. Everything else is automatically handled.
* @returns Promise - tries to resolve with async WS API response. Rejects if disconnected or exception is seen in async WS API response
*/
// This overload allows the caller to omit the 3rd param, if it isn't required
sendWSAPIRequest<
TWSKey extends keyof WsAPIWsKeyTopicMap,
TWSOperation extends WsAPIWsKeyTopicMap[TWSKey],
TWSParams extends Exact<WsAPITopicRequestParamMap[TWSOperation]>,
>(
wsKey: TWSKey,
operation: TWSOperation,
...params: TWSParams extends undefined ? [] : [TWSParams]
): Promise<WsAPIOperationResponseMap[TWSOperation]>;
// These overloads give stricter types than mapped generics, since generic constraints do not trigger excess property checks
// Without these overloads, TypeScript won't complain if you include an unexpected property with your request (if it doesn't clash with an existing property)
sendWSAPIRequest(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: 'order.create',
params: WsAPITopicRequestParamMap['order.create'],
): Promise<WsAPIOperationResponseMap['order.create']>;
sendWSAPIRequest(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: 'order.amend',
params: WsAPITopicRequestParamMap['order.amend'],
): Promise<WsAPIOperationResponseMap['order.amend']>;
sendWSAPIRequest(
wsKey: typeof WS_KEY_MAP.v5PrivateTrade,
operation: 'order.cancel',
params: WsAPITopicRequestParamMap['order.cancel'],
): Promise<WsAPIOperationResponseMap['order.cancel']>;
async sendWSAPIRequest<
TWSKey extends keyof WsAPIWsKeyTopicMap,
TWSOperation extends WsAPIWsKeyTopicMap[TWSKey],
TWSParams extends Exact<WsAPITopicRequestParamMap[TWSOperation]>,
TWSAPIResponse extends
WsAPIOperationResponseMap[TWSOperation] = WsAPIOperationResponseMap[TWSOperation],
>(
wsKey: WsKey = WS_KEY_MAP.v5PrivateTrade,
operation: TWSOperation,
params: TWSParams,
): Promise<WsAPIOperationResponseMap[TWSOperation]> {
this.logger.trace(`sendWSAPIRequest(): assert "${wsKey}" is connected`);
await this.assertIsConnected(wsKey);
this.logger.trace('sendWSAPIRequest()->assertIsConnected() ok');
await this.assertIsAuthenticated(wsKey);
this.logger.trace('sendWSAPIRequest()->assertIsAuthenticated() ok');
const requestEvent: WSAPIRequest<TWSParams> = {
reqId: this.getNewRequestId(),
header: {
'X-BAPI-RECV-WINDOW': `${this.options.recvWindow}`,
'X-BAPI-TIMESTAMP': `${Date.now()}`,
Referer: APIID,
},
op: operation,
args: [params],
};
// Sign, if needed
const signedEvent = await this.signWSAPIRequest(requestEvent);
// Store deferred promise, resolved within the "resolveEmittableEvents" method while parsing incoming events
const promiseRef = getPromiseRefForWSAPIRequest(requestEvent);
const deferredPromise =
this.getWsStore().createDeferredPromise<TWSAPIResponse>(
wsKey,
promiseRef,
false,
);
this.logger.trace(
`sendWSAPIRequest(): sending raw request: ${JSON.stringify(signedEvent, null, 2)}`,
);
// Send event
this.tryWsSend(wsKey, JSON.stringify(signedEvent));
this.logger.trace(`sendWSAPIRequest(): sent ${operation} event`);
// Return deferred promise, so caller can await this call
return deferredPromise.promise!;
}
}