bootstrap public spot test
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { KlineInterval } from './types/shared';
|
||||
import { APIResponse, KlineInterval } from './types/shared';
|
||||
import {
|
||||
NewSpotOrder,
|
||||
OrderSide,
|
||||
OrderTypeSpot,
|
||||
SpotOrderQueryById,
|
||||
SpotSymbolInfo,
|
||||
} from './types/spot';
|
||||
import BaseRestClient from './util/BaseRestClient';
|
||||
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');
|
||||
}
|
||||
|
||||
getOrderBook(symbol: string, limit?: number) {
|
||||
getOrderBook(symbol: string, limit?: number): Promise<APIResponse<any>> {
|
||||
return this.get('/spot/quote/v1/depth', {
|
||||
symbol,
|
||||
limit,
|
||||
});
|
||||
}
|
||||
|
||||
getMergedOrderBook(symbol: string, scale?: number, limit?: number) {
|
||||
getMergedOrderBook(
|
||||
symbol: string,
|
||||
scale?: number,
|
||||
limit?: number
|
||||
): Promise<APIResponse<any>> {
|
||||
return this.get('/spot/quote/v1/depth/merged', {
|
||||
symbol,
|
||||
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', {
|
||||
symbol,
|
||||
limit,
|
||||
@@ -87,7 +92,7 @@ export class SpotClient extends BaseRestClient {
|
||||
limit?: number,
|
||||
startTime?: number,
|
||||
endTime?: number
|
||||
) {
|
||||
): Promise<APIResponse<any[]>> {
|
||||
return this.get('/spot/quote/v1/kline', {
|
||||
symbol,
|
||||
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 });
|
||||
}
|
||||
|
||||
getLastTradedPrice(symbol?: string) {
|
||||
getLastTradedPrice(symbol?: string): Promise<APIResponse<any>> {
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -171,4 +176,26 @@ export class SpotClient extends BaseRestClient {
|
||||
getBalances() {
|
||||
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> {
|
||||
ret_code: number;
|
||||
ret_msg: 'OK' | string;
|
||||
ext_code: string;
|
||||
ext_info: string;
|
||||
ext_code: string | null;
|
||||
ext_info: string | null;
|
||||
result: T;
|
||||
}
|
||||
|
||||
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
||||
/** UTC timestamp */
|
||||
time_now: string;
|
||||
time_now: numberInString;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { numberInString } from './shared';
|
||||
|
||||
export type OrderSide = 'Buy' | 'Sell';
|
||||
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
||||
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
||||
@@ -16,3 +18,18 @@ export interface SpotOrderQueryById {
|
||||
orderId?: 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 {
|
||||
ws?: WebSocket;
|
||||
connectionState?: WsConnectionState;
|
||||
activePingTimer?: NodeJS.Timeout | undefined;
|
||||
activePongTimer?: NodeJS.Timeout | undefined;
|
||||
activePingTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||
activePongTimer?: ReturnType<typeof setTimeout> | undefined;
|
||||
subscribedTopics: WsTopicList;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default class WsStore {
|
||||
private wsState: Record<string, WsStoredState>
|
||||
private wsState: Record<string, WsStoredState>;
|
||||
private logger: typeof DefaultLogger;
|
||||
|
||||
constructor(logger: typeof DefaultLogger) {
|
||||
@@ -40,11 +39,14 @@ export default class WsStore {
|
||||
|
||||
create(key: string): WsStoredState | undefined {
|
||||
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] = {
|
||||
subscribedTopics: new Set(),
|
||||
connectionState: WsConnectionState.READY_STATE_INITIAL
|
||||
connectionState: WsConnectionState.READY_STATE_INITIAL,
|
||||
};
|
||||
return this.get(key);
|
||||
}
|
||||
@@ -52,7 +54,10 @@ export default class WsStore {
|
||||
delete(key: string) {
|
||||
if (this.hasExistingActiveConnection(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();
|
||||
}
|
||||
delete this.wsState[key];
|
||||
@@ -70,7 +75,10 @@ export default class WsStore {
|
||||
|
||||
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
||||
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;
|
||||
return wsConnection;
|
||||
@@ -80,7 +88,10 @@ export default class WsStore {
|
||||
|
||||
isWsOpen(key: string): boolean {
|
||||
const existingConnection = this.getWs(key);
|
||||
return !!existingConnection && existingConnection.readyState === existingConnection.OPEN;
|
||||
return (
|
||||
!!existingConnection &&
|
||||
existingConnection.readyState === existingConnection.OPEN
|
||||
);
|
||||
}
|
||||
|
||||
getConnectionState(key: string): WsConnectionState {
|
||||
|
||||
@@ -1,26 +1,19 @@
|
||||
|
||||
export function successResponseList() {
|
||||
export function successResponseList(successMsg: string | null = 'OK') {
|
||||
return {
|
||||
"ext_code": "",
|
||||
"ext_info": "",
|
||||
"result": expect.any(Array),
|
||||
"ret_code": 0,
|
||||
"ret_msg": "OK",
|
||||
"time_now": expect.any(String),
|
||||
result: expect.any(Array),
|
||||
ret_code: 0,
|
||||
ret_msg: successMsg,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function successResponseObject() {
|
||||
export function successResponseObject(successMsg: string | null = 'OK') {
|
||||
return {
|
||||
"ext_code": "",
|
||||
"ext_info": "",
|
||||
"result": expect.any(Object),
|
||||
"ret_code": 0,
|
||||
"ret_msg": "OK",
|
||||
"time_now": expect.any(String),
|
||||
result: expect.any(Object),
|
||||
ret_code: 0,
|
||||
ret_msg: successMsg,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function notAuthenticatedError() {
|
||||
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