diff --git a/src/constants/enum.ts b/src/constants/enum.ts index 06aee36..eca2751 100644 --- a/src/constants/enum.ts +++ b/src/constants/enum.ts @@ -9,3 +9,34 @@ export const positionTpSlModeEnum = { /** Partial take profit/stop loss mode (multiple TP and SL orders can be placed, covering portions of the position) */ Partial: 'Partial', } as const; + +export const API_ERROR_CODE = { + BALANCE_INSUFFICIENT_SPOT: -1131, + ORDER_NOT_FOUND_OR_TOO_LATE_SPOT: -2013, + /** This could mean bad request, incorrect value types or even incorrect/missing values */ + PARAMS_MISSING_OR_WRONG: 10001, + ORDER_NOT_FOUND_OR_TOO_LATE: 20001, + POSITION_STATUS_NOT_NORMAL: 30013, + CANNOT_SET_TRADING_STOP_FOR_ZERO_POS: 30024, + /** Seen when placing an order */ + INSUFFICIENT_BALANCE_FOR_ORDER_COST: 30031, + POSITION_IDX_NOT_MATCH_POSITION_MODE: 30041, + /** Seen if a conditional order is too large */ + INSUFFICIENT_BALANCE: 30042, + /** E.g. trying to change position margin while on cross */ + POSITION_IS_CROSS_MARGIN: 30056, + POSITION_MODE_NOT_MODIFIED: 30083, + ISOLATED_NOT_MODIFIED: 30084, + RISK_LIMIT_NOT_EXISTS: 30090, + LEVERAGE_NOT_MODIFIED: 34036, + SAME_SLTP_MODE: 37002, + ORDER_NOT_FOUND_OR_TOO_LATE_LINEAR: 130010, + ORDER_COST_NOT_AVAILABLE: 130021, + CANNOT_SET_LINEAR_TRADING_STOP_FOR_ZERO_POS: 130024, + ISOLATED_NOT_MODIFIED_LINEAR: 130056, + POSITION_SIZE_IS_ZERO: 130057, + AUTO_ADD_MARGIN_NOT_MODIFIED: 130060, + INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080, + SAME_SLTP_MODE_LINEAR: 130150, + RISK_ID_NOT_MODIFIED: 134026, +} as const; diff --git a/src/index.ts b/src/index.ts index 941d3ea..75c64d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,5 +7,4 @@ export * from './logger'; export * from './types/shared'; export * from './types/spot'; export * from './util/WsStore'; -export * from './util/enum'; export * from './constants/enum'; diff --git a/src/spot-client.ts b/src/spot-client.ts index 9b1b7a9..9dff7c5 100644 --- a/src/spot-client.ts +++ b/src/spot-client.ts @@ -9,6 +9,7 @@ import { } from './types/spot'; import BaseRestClient from './util/BaseRestClient'; import { + agentSource, getRestBaseUrl, RestClientOptions, REST_CLIENT_TYPE_ENUM, @@ -121,7 +122,10 @@ export class SpotClient extends BaseRestClient { */ submitOrder(params: NewSpotOrder): Promise> { - return this.postPrivate('/spot/v1/order', params); + return this.postPrivate('/spot/v1/order', { + ...params, + agentSource, + }); } getOrder(params: SpotOrderQueryById): Promise> { diff --git a/src/util/BaseRestClient.ts b/src/util/BaseRestClient.ts index a1a2562..790c297 100644 --- a/src/util/BaseRestClient.ts +++ b/src/util/BaseRestClient.ts @@ -7,6 +7,7 @@ import { serializeParams, RestClientType, REST_CLIENT_TYPE_ENUM, + agentSource, } from './requestUtils'; // axios.interceptors.request.use((request) => { @@ -162,8 +163,13 @@ export default abstract class BaseRestClient { const signResult = await this.prepareSignParams(params, isPublicApi); - if (method === 'GET') { + if (method === 'GET' || this.isSpotClient()) { options.params = signResult.paramsWithSign; + if (options.params?.agentSource) { + options.data = { + agentSource: agentSource, + }; + } } else { options.data = signResult.paramsWithSign; } diff --git a/src/util/enum.ts b/src/util/enum.ts deleted file mode 100644 index fb36dc5..0000000 --- a/src/util/enum.ts +++ /dev/null @@ -1,27 +0,0 @@ -export const API_ERROR_CODE = { - PARAMS_MISSING_OR_WRONG: 10001, - ORDER_NOT_FOUND_OR_TOO_LATE: 20001, - POSITION_STATUS_NOT_NORMAL: 30013, - CANNOT_SET_TRADING_STOP_FOR_ZERO_POS: 30024, - /** Seen when placing an order */ - INSUFFICIENT_BALANCE_FOR_ORDER_COST: 30031, - POSITION_IDX_NOT_MATCH_POSITION_MODE: 30041, - /** Seen if a conditional order is too large */ - INSUFFICIENT_BALANCE: 30042, - /** E.g. trying to change position margin while on cross */ - POSITION_IS_CROSS_MARGIN: 30056, - POSITION_MODE_NOT_MODIFIED: 30083, - ISOLATED_NOT_MODIFIED: 30084, - RISK_LIMIT_NOT_EXISTS: 30090, - LEVERAGE_NOT_MODIFIED: 34036, - SAME_SLTP_MODE: 37002, - ORDER_NOT_FOUND_OR_TOO_LATE_LINEAR: 130010, - ORDER_COST_NOT_AVAILABLE: 130021, - CANNOT_SET_LINEAR_TRADING_STOP_FOR_ZERO_POS: 130024, - ISOLATED_NOT_MODIFIED_LINEAR: 130056, - POSITION_SIZE_IS_ZERO: 130057, - AUTO_ADD_MARGIN_NOT_MODIFIED: 130060, - INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080, - SAME_SLTP_MODE_LINEAR: 130150, - RISK_ID_NOT_MODIFIED: 134026, -} as const; diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index d2cf584..621fb3b 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -85,6 +85,8 @@ export function isWsPong(response: any) { ); } +export const agentSource = 'bybitapinode'; + export const REST_CLIENT_TYPE_ENUM = { inverse: 'inverse', inverseFutures: 'inverseFutures', diff --git a/test/inverse-futures/private.write.test.ts b/test/inverse-futures/private.write.test.ts index f79707c..a2b6965 100644 --- a/test/inverse-futures/private.write.test.ts +++ b/test/inverse-futures/private.write.test.ts @@ -1,5 +1,4 @@ -import { InverseFuturesClient } from '../../src'; -import { API_ERROR_CODE } from '../../src/util/enum'; +import { API_ERROR_CODE, InverseFuturesClient } from '../../src'; import { successResponseObject } from '../response.util'; describe('Private Inverse-Futures REST API POST Endpoints', () => { diff --git a/test/inverse/private.write.test.ts b/test/inverse/private.write.test.ts index 8577c3d..2153c33 100644 --- a/test/inverse/private.write.test.ts +++ b/test/inverse/private.write.test.ts @@ -1,6 +1,6 @@ +import { API_ERROR_CODE } from '../../src'; import { InverseClient } from '../../src/inverse-client'; -import { API_ERROR_CODE } from '../../src/util/enum'; -import { successResponseList, successResponseObject } from '../response.util'; +import { successResponseObject } from '../response.util'; describe('Private Inverse REST API Endpoints', () => { const useLivenet = true; diff --git a/test/linear/private.write.test.ts b/test/linear/private.write.test.ts index dec3a40..9a987ae 100644 --- a/test/linear/private.write.test.ts +++ b/test/linear/private.write.test.ts @@ -1,5 +1,4 @@ -import { LinearClient } from '../../src'; -import { API_ERROR_CODE } from '../../src/util/enum'; +import { API_ERROR_CODE, LinearClient } from '../../src'; import { successResponseObject } from '../response.util'; describe('Private Inverse-Futures REST API POST Endpoints', () => { diff --git a/test/spot/private.write.test.ts b/test/spot/private.write.test.ts new file mode 100644 index 0000000..811d9e4 --- /dev/null +++ b/test/spot/private.write.test.ts @@ -0,0 +1,56 @@ +import { API_ERROR_CODE, SpotClient } from '../../src'; +import { successResponseObject } from '../response.util'; + +describe('Private Inverse-Futures REST API POST Endpoints', () => { + const useLivenet = true; + const API_KEY = process.env.API_KEY_COM; + const API_SECRET = process.env.API_SECRET_COM; + + it('should have api credentials to test with', () => { + expect(API_KEY).toStrictEqual(expect.any(String)); + expect(API_SECRET).toStrictEqual(expect.any(String)); + }); + + const api = new SpotClient(API_KEY, API_SECRET, useLivenet, { + disable_time_sync: true, + }); + + // Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol! + const symbol = 'BTCUSDT'; + + // These tests are primarily check auth is working by expecting balance or order not found style errors + + it('submitOrder()', async () => { + expect( + await api.submitOrder({ + side: 'Buy', + symbol, + qty: 10000, + type: 'MARKET', + }) + ).toMatchObject({ + ret_code: API_ERROR_CODE.BALANCE_INSUFFICIENT_SPOT, + ret_msg: 'Balance insufficient ', + }); + }); + + it('cancelOrder()', async () => { + expect( + await api.cancelOrder({ + orderId: '1231231', + }) + ).toMatchObject({ + ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE_SPOT, + ret_msg: 'Order does not exist.', + }); + }); + + it('cancelOrderBatch()', async () => { + expect( + await api.cancelOrderBatch({ + symbol, + orderTypes: ['LIMIT', 'LIMIT_MAKER'], + }) + ).toMatchObject(successResponseObject('')); + }); +});