bootstrap public spot test
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { KlineInterval } from './types/shared';
|
import { APIResponse, KlineInterval } from './types/shared';
|
||||||
import {
|
import {
|
||||||
NewSpotOrder,
|
NewSpotOrder,
|
||||||
OrderSide,
|
OrderSide,
|
||||||
OrderTypeSpot,
|
OrderTypeSpot,
|
||||||
SpotOrderQueryById,
|
SpotOrderQueryById,
|
||||||
|
SpotSymbolInfo,
|
||||||
} from './types/spot';
|
} from './types/spot';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
import { getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
import { getRestBaseUrl, RestClientOptions } from './util/requestUtils';
|
||||||
@@ -55,18 +56,22 @@ export class SpotClient extends BaseRestClient {
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
|
|
||||||
getSymbols() {
|
getSymbols(): Promise<APIResponse<SpotSymbolInfo[]>> {
|
||||||
return this.get('/spot/v1/symbols');
|
return this.get('/spot/v1/symbols');
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrderBook(symbol: string, limit?: number) {
|
getOrderBook(symbol: string, limit?: number): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/depth', {
|
return this.get('/spot/quote/v1/depth', {
|
||||||
symbol,
|
symbol,
|
||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMergedOrderBook(symbol: string, scale?: number, limit?: number) {
|
getMergedOrderBook(
|
||||||
|
symbol: string,
|
||||||
|
scale?: number,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/depth/merged', {
|
return this.get('/spot/quote/v1/depth/merged', {
|
||||||
symbol,
|
symbol,
|
||||||
scale,
|
scale,
|
||||||
@@ -74,7 +79,7 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrades(symbol: string, limit?: number) {
|
getTrades(symbol: string, limit?: number): Promise<APIResponse<any[]>> {
|
||||||
return this.get('/spot/v1/trades', {
|
return this.get('/spot/v1/trades', {
|
||||||
symbol,
|
symbol,
|
||||||
limit,
|
limit,
|
||||||
@@ -87,7 +92,7 @@ export class SpotClient extends BaseRestClient {
|
|||||||
limit?: number,
|
limit?: number,
|
||||||
startTime?: number,
|
startTime?: number,
|
||||||
endTime?: number
|
endTime?: number
|
||||||
) {
|
): Promise<APIResponse<any[]>> {
|
||||||
return this.get('/spot/quote/v1/kline', {
|
return this.get('/spot/quote/v1/kline', {
|
||||||
symbol,
|
symbol,
|
||||||
interval,
|
interval,
|
||||||
@@ -97,15 +102,15 @@ export class SpotClient extends BaseRestClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get24hrTicker(symbol?: string) {
|
get24hrTicker(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
|
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastTradedPrice(symbol?: string) {
|
getLastTradedPrice(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/price', { symbol });
|
return this.get('/spot/quote/v1/ticker/price', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
getBestBidAskPrice(symbol?: string) {
|
getBestBidAskPrice(symbol?: string): Promise<APIResponse<any>> {
|
||||||
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
|
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,4 +176,26 @@ export class SpotClient extends BaseRestClient {
|
|||||||
getBalances() {
|
getBalances() {
|
||||||
return this.getPrivate('/spot/v1/account');
|
return this.getPrivate('/spot/v1/account');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leveraged Token Endpoints
|
||||||
|
*/
|
||||||
|
|
||||||
|
getLeveragedTokenAssetInfo(
|
||||||
|
leverageTokenCode: string,
|
||||||
|
timestamp?: number
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
|
return this.get('/spot/lt/v1/info', {
|
||||||
|
ltCode: leverageTokenCode,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getLeveragedTokenMarketInfo(
|
||||||
|
leverageTokenCode: string
|
||||||
|
): Promise<APIResponse<any>> {
|
||||||
|
return this.get('/spot/lt/v1/reference', {
|
||||||
|
ltCode: leverageTokenCode,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ export type numberInString = string;
|
|||||||
export interface APIResponse<T> {
|
export interface APIResponse<T> {
|
||||||
ret_code: number;
|
ret_code: number;
|
||||||
ret_msg: 'OK' | string;
|
ret_msg: 'OK' | string;
|
||||||
ext_code: string;
|
ext_code: string | null;
|
||||||
ext_info: string;
|
ext_info: string | null;
|
||||||
result: T;
|
result: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
||||||
/** UTC timestamp */
|
/** UTC timestamp */
|
||||||
time_now: string;
|
time_now: numberInString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { numberInString } from './shared';
|
||||||
|
|
||||||
export type OrderSide = 'Buy' | 'Sell';
|
export type OrderSide = 'Buy' | 'Sell';
|
||||||
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
||||||
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
||||||
@@ -16,3 +18,18 @@ export interface SpotOrderQueryById {
|
|||||||
orderId?: string;
|
orderId?: string;
|
||||||
orderLinkId?: string;
|
orderLinkId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SpotSymbolInfo {
|
||||||
|
name: string;
|
||||||
|
alias: string;
|
||||||
|
baseCurrency: string;
|
||||||
|
quoteCurrency: string;
|
||||||
|
basePrecision: numberInString;
|
||||||
|
quotePrecision: numberInString;
|
||||||
|
minTradeQuantity: numberInString;
|
||||||
|
minTradeAmount: numberInString;
|
||||||
|
minPricePrecision: numberInString;
|
||||||
|
maxTradeQuantity: numberInString;
|
||||||
|
maxTradeAmount: numberInString;
|
||||||
|
category: numberInString;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,14 +9,13 @@ type WsTopicList = Set<WsTopic>;
|
|||||||
interface WsStoredState {
|
interface WsStoredState {
|
||||||
ws?: WebSocket;
|
ws?: WebSocket;
|
||||||
connectionState?: WsConnectionState;
|
connectionState?: WsConnectionState;
|
||||||
activePingTimer?: NodeJS.Timeout | undefined;
|
activePingTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||||
activePongTimer?: NodeJS.Timeout | undefined;
|
activePongTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||||
subscribedTopics: WsTopicList;
|
subscribedTopics: WsTopicList;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class WsStore {
|
export default class WsStore {
|
||||||
private wsState: Record<string, WsStoredState>
|
private wsState: Record<string, WsStoredState>;
|
||||||
private logger: typeof DefaultLogger;
|
private logger: typeof DefaultLogger;
|
||||||
|
|
||||||
constructor(logger: typeof DefaultLogger) {
|
constructor(logger: typeof DefaultLogger) {
|
||||||
@@ -40,11 +39,14 @@ export default class WsStore {
|
|||||||
|
|
||||||
create(key: string): WsStoredState | undefined {
|
create(key: string): WsStoredState | undefined {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
this.logger.warning(
|
||||||
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
|
this.getWs(key)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.wsState[key] = {
|
this.wsState[key] = {
|
||||||
subscribedTopics: new Set(),
|
subscribedTopics: new Set(),
|
||||||
connectionState: WsConnectionState.READY_STATE_INITIAL
|
connectionState: WsConnectionState.READY_STATE_INITIAL,
|
||||||
};
|
};
|
||||||
return this.get(key);
|
return this.get(key);
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,10 @@ export default class WsStore {
|
|||||||
delete(key: string) {
|
delete(key: string) {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
const ws = this.getWs(key);
|
const ws = this.getWs(key);
|
||||||
this.logger.warning('WsStore deleting state for connection still open: ', ws);
|
this.logger.warning(
|
||||||
|
'WsStore deleting state for connection still open: ',
|
||||||
|
ws
|
||||||
|
);
|
||||||
ws?.close();
|
ws?.close();
|
||||||
}
|
}
|
||||||
delete this.wsState[key];
|
delete this.wsState[key];
|
||||||
@@ -70,7 +75,10 @@ export default class WsStore {
|
|||||||
|
|
||||||
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
||||||
if (this.isWsOpen(key)) {
|
if (this.isWsOpen(key)) {
|
||||||
this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key));
|
this.logger.warning(
|
||||||
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
|
this.getWs(key)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.get(key, true)!.ws = wsConnection;
|
this.get(key, true)!.ws = wsConnection;
|
||||||
return wsConnection;
|
return wsConnection;
|
||||||
@@ -80,7 +88,10 @@ export default class WsStore {
|
|||||||
|
|
||||||
isWsOpen(key: string): boolean {
|
isWsOpen(key: string): boolean {
|
||||||
const existingConnection = this.getWs(key);
|
const existingConnection = this.getWs(key);
|
||||||
return !!existingConnection && existingConnection.readyState === existingConnection.OPEN;
|
return (
|
||||||
|
!!existingConnection &&
|
||||||
|
existingConnection.readyState === existingConnection.OPEN
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnectionState(key: string): WsConnectionState {
|
getConnectionState(key: string): WsConnectionState {
|
||||||
|
|||||||
@@ -1,26 +1,19 @@
|
|||||||
|
export function successResponseList(successMsg: string | null = 'OK') {
|
||||||
export function successResponseList() {
|
|
||||||
return {
|
return {
|
||||||
"ext_code": "",
|
result: expect.any(Array),
|
||||||
"ext_info": "",
|
ret_code: 0,
|
||||||
"result": expect.any(Array),
|
ret_msg: successMsg,
|
||||||
"ret_code": 0,
|
|
||||||
"ret_msg": "OK",
|
|
||||||
"time_now": expect.any(String),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function successResponseObject() {
|
export function successResponseObject(successMsg: string | null = 'OK') {
|
||||||
return {
|
return {
|
||||||
"ext_code": "",
|
result: expect.any(Object),
|
||||||
"ext_info": "",
|
ret_code: 0,
|
||||||
"result": expect.any(Object),
|
ret_msg: successMsg,
|
||||||
"ret_code": 0,
|
|
||||||
"ret_msg": "OK",
|
|
||||||
"time_now": expect.any(String),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function notAuthenticatedError() {
|
export function notAuthenticatedError() {
|
||||||
return new Error('Private endpoints require api and private keys set');
|
return new Error('Private endpoints require api and private keys set');
|
||||||
};
|
}
|
||||||
|
|||||||
37
test/spot/public.test.ts
Normal file
37
test/spot/public.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { SpotClient } from '../../src';
|
||||||
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseList,
|
||||||
|
successResponseObject,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Spot REST API Endpoints', () => {
|
||||||
|
const useLivenet = true;
|
||||||
|
const api = new SpotClient(undefined, undefined, useLivenet, {
|
||||||
|
disable_time_sync: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const interval = '15';
|
||||||
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
|
expect(() => api.getOpenOrders()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
expect(() => api.getBalances()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbols()', async () => {
|
||||||
|
expect(await api.getSymbols()).toMatchObject(successResponseList(''));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObject(null)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user