feat(): add plan order endpoints

This commit is contained in:
Tiago Siebler
2023-03-22 15:00:40 +00:00
parent bf2e545d09
commit 31aee2a0e5
6 changed files with 464 additions and 202 deletions

View File

@@ -9,6 +9,15 @@ import {
SymbolRules,
NewSpotSubTransfer,
NewSpotWithdraw,
CancelSpotOrderV2,
BatchCancelSpotOrderV2,
SpotOrderResult,
NewSpotPlanOrder,
ModifySpotPlanOrder,
CancelSpotPlanOrderParams,
GetSpotPlanOrdersParams,
SpotPlanOrder,
GetHistoricPlanOrdersParams,
} from './types';
import { REST_CLIENT_TYPE_ENUM } from './util';
import BaseRestClient from './util/BaseRestClient';
@@ -246,7 +255,7 @@ export class SpotClient extends BaseRestClient {
*/
/** Place order */
submitOrder(params: NewSpotOrder): Promise<APIResponse<any>> {
submitOrder(params: NewSpotOrder): Promise<APIResponse<SpotOrderResult>> {
return this.postPrivate('/api/spot/v1/trade/orders', params);
}
@@ -269,6 +278,20 @@ export class SpotClient extends BaseRestClient {
});
}
/** Cancel order (v2 endpoint - supports orderId or clientOid) */
cancelOrderV2(params?: CancelSpotOrderV2): Promise<APIResponse<any>> {
return this.postPrivate('/api/spot/v1/trade/cancel-order-v2', params);
}
/**
* Cancel all spot orders for a symbol
*/
cancelSymbolOrders(symbol: string): Promise<APIResponse<any>> {
return this.postPrivate('/api/spot/v1/trade/cancel-symbol-order', {
symbol,
});
}
/** Cancel order in batch (per symbol) */
batchCancelOrder(
symbol: string,
@@ -280,6 +303,16 @@ export class SpotClient extends BaseRestClient {
});
}
/** Cancel order in batch (per symbol). V2 endpoint, supports orderIds or clientOids. */
batchCancelOrderV2(
params: BatchCancelSpotOrderV2
): Promise<APIResponse<any>> {
return this.postPrivate(
'/api/spot/v1/trade/cancel-batch-orders-v2',
params
);
}
/** Get order details */
getOrder(
symbol: string,
@@ -321,4 +354,49 @@ export class SpotClient extends BaseRestClient {
...pagination,
});
}
/** Place plan order */
submitPlanOrder(
params: NewSpotPlanOrder
): Promise<APIResponse<SpotOrderResult>> {
return this.postPrivate('/api/spot/v1/plan/placePlan', params);
}
/** Modify plan order */
modifyPlanOrder(
params: ModifySpotPlanOrder
): Promise<APIResponse<SpotOrderResult>> {
return this.postPrivate('/api/spot/v1/plan/modifyPlan', params);
}
/** Cancel plan order */
cancelPlanOrder(
params: CancelSpotPlanOrderParams
): Promise<APIResponse<string>> {
return this.postPrivate('/api/spot/v1/plan/cancelPlan', params);
}
/** Get current plan orders */
getCurrentPlanOrders(params: GetSpotPlanOrdersParams): Promise<
APIResponse<{
nextFlag: boolean;
endId: number;
orderList: SpotPlanOrder[];
}>
> {
return this.postPrivate('/api/spot/v1/plan/currentPlan', params);
}
/** Get history plan orders */
getHistoricPlanOrders(params: GetHistoricPlanOrdersParams): Promise<
APIResponse<{
nextFlag: boolean;
endId: number;
orderList: SpotPlanOrder[];
}>
> {
return this.postPrivate('/api/spot/v1/plan/historyPlan', params);
}
//
}

View File

@@ -10,18 +10,6 @@ export interface NewWalletTransfer {
clientOid?: string;
}
export interface NewSpotOrder {
symbol: string;
side: 'buy' | 'sell';
orderType: 'limit' | 'market';
force: OrderTimeInForce;
price?: string;
quantity: string;
clientOrderId?: string;
}
export type NewBatchSpotOrder = Omit<NewSpotOrder, 'symbol'>;
export interface NewSpotSubTransfer {
fromType: WalletType;
toType: WalletType;
@@ -41,3 +29,79 @@ export interface NewSpotWithdraw {
remark?: string;
clientOid?: string;
}
export interface NewSpotOrder {
symbol: string;
side: 'buy' | 'sell';
orderType: 'limit' | 'market';
force: OrderTimeInForce;
price?: string;
quantity: string;
clientOrderId?: string;
}
export type NewBatchSpotOrder = Omit<NewSpotOrder, 'symbol'>;
export interface CancelSpotOrderV2 {
symbol: string;
orderId?: string;
clientOid?: string;
}
export interface BatchCancelSpotOrderV2 {
symbol: string;
orderIds?: string[];
clientOids?: string[];
}
export interface NewSpotPlanOrder {
symbol: string;
side: 'buy' | 'sell';
triggerPrice: number;
executePrice?: number;
size: number;
triggerType: 'fill_price' | 'market_price';
orderType: 'limit' | 'market';
clientOid?: string;
timeInForceValue?: string;
}
export interface NewSpotPlanOrder {
symbol: string;
side: 'buy' | 'sell';
triggerPrice: number;
executePrice?: number;
size: number;
triggerType: 'fill_price' | 'market_price';
orderType: 'limit' | 'market';
clientOid?: string;
timeInForceValue?: string;
}
export interface ModifySpotPlanOrder {
orderId?: string;
clientOid?: string;
triggerPrice: number;
executePrice?: number;
size?: string;
orderType: 'limit' | 'market';
}
export interface CancelSpotPlanOrderParams {
orderId?: string;
clientOid?: string;
}
export interface GetSpotPlanOrdersParams {
symbol: string;
pageSize: string;
lastEndId?: string;
}
export interface GetHistoricPlanOrdersParams {
symbol: string;
pageSize: string;
lastEndId?: string;
startTime: string;
endTime: string;
}

View File

@@ -20,3 +20,23 @@ export interface SymbolRules {
quantityScale: string;
status: string;
}
export interface SpotOrderResult {
orderId: string;
clientOrderId: string;
}
export interface SpotPlanOrder {
orderId: string;
clientOid: string;
symbol: string;
size: string;
executePrice: string;
triggerPrice: string;
status: string;
orderType: string;
side: string;
triggerType: string;
enterPointSource: string;
cTime: number;
}

View File

@@ -309,7 +309,6 @@ export default abstract class BaseRestClient {
'ACCESS-PASSPHRASE': this.apiPass,
'ACCESS-TIMESTAMP': signResult.timestamp,
'ACCESS-SIGN': signResult.sign,
'Content-Type': 'application/json',
};
if (method === 'GET') {

View File

@@ -159,4 +159,45 @@ describe('Private Spot REST API GET Endpoints', () => {
expect(e).toBeNull();
}
});
it('getCurrentPlanOrders()', async () => {
try {
expect(
await api.getCurrentPlanOrders({ symbol, pageSize: '20' })
).toMatchObject({
...sucessEmptyResponseObject(),
data: {
endId: null,
nextFlag: false,
orderList: expect.any(Array),
},
});
} catch (e) {
console.error('getCurrentPlanOrders: ', e);
expect(e).toBeNull();
}
});
it('getHistoricPlanOrders()', async () => {
try {
expect(
await api.getHistoricPlanOrders({
symbol,
pageSize: '20',
startTime: '1667889483000',
endTime: '1668134732000',
})
).toMatchObject({
...sucessEmptyResponseObject(),
data: {
endId: null,
nextFlag: false,
orderList: expect.any(Array),
},
});
} catch (e) {
console.error('getHistoricPlanOrders: ', e);
expect(e).toBeNull();
}
});
});

View File

@@ -20,10 +20,8 @@ describe('Private Spot REST API POST Endpoints', () => {
const symbol = 'BTCUSDT_SPBL';
const coin = 'USDT';
const timestampOneHourAgo = new Date().getTime() - 1000 * 60 * 60;
const from = timestampOneHourAgo.toFixed(0);
const to = String(Number(from) + 1000 * 60 * 30); // 30 minutes
describe('transfers', () => {
it('transfer()', async () => {
try {
expect(
@@ -79,7 +77,7 @@ describe('Private Spot REST API POST Endpoints', () => {
// console.error('transferV2: ', e);
expect(e.body).toMatchObject({
// not sure what this error means, probably no balance. Seems to change?
code: expect.stringMatching(/42013|43117/gim),
code: expect.stringMatching(/42013|43117|40018/gim),
});
}
});
@@ -149,7 +147,8 @@ describe('Private Spot REST API POST Endpoints', () => {
});
}
});
});
describe('orders', () => {
it('submitOrder()', async () => {
try {
expect(
@@ -219,4 +218,65 @@ describe('Private Spot REST API POST Endpoints', () => {
});
}
});
});
describe('plan orders', () => {
let planOrderId: string;
it('submitPlanOrder()', async () => {
try {
const result = await api.submitPlanOrder({
symbol,
side: 'buy',
orderType: 'market',
size: 100,
triggerPrice: 100,
triggerType: 'fill_price',
});
planOrderId = result.data.orderId;
expect(result).toMatchObject({
...sucessEmptyResponseObject(),
});
} catch (e) {
console.error('submitPlanOrder(): ', e);
expect(e).toBeNull();
}
});
it('modifyPlanOrder()', async () => {
try {
expect(
await api.modifyPlanOrder({
orderType: 'market',
triggerPrice: 100,
orderId: '123456',
})
).toMatchObject({
...sucessEmptyResponseObject(),
data: expect.any(Array),
});
} catch (e) {
expect(e.body).toMatchObject({
code: API_ERROR_CODE.PLAN_ORDER_NOT_FOUND,
});
}
});
it('cancelPlanOrder()', async () => {
try {
expect(
await api.cancelPlanOrder({
orderId: planOrderId || '123456',
})
).toMatchObject({
...sucessEmptyResponseObject(),
data: expect.any(String),
});
} catch (e) {
console.error('cancelPlanOrder(): ', e);
expect(e).toBeNull();
}
});
});
});