bootstrap public spot test

This commit is contained in:
tiagosiebler
2022-05-05 22:45:38 +01:00
parent 0b8bed8faf
commit a7aaedac41
6 changed files with 126 additions and 41 deletions

View File

@@ -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,
});
}
}

View File

@@ -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;
}
/**

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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
View 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)
);
});
});