From 22c26c3d1978d7f6d419e0073e6bd9aedb481d10 Mon Sep 17 00:00:00 2001 From: Zaixin Cheng Date: Tue, 12 May 2026 05:28:28 -0400 Subject: [PATCH] feat: add historical transaction orders endpoint --- README.md | 2 +- ...torical-transaction-orders.service.spec.ts | 91 ++++++++++++++++ src/bingx-client/services/trade.service.ts | 13 +++ ...-historical-transaction-orders-endpoint.ts | 100 ++++++++++++++++++ src/bingx/endpoints/index.ts | 1 + 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/bingx-client/services/trade-historical-transaction-orders.service.spec.ts create mode 100644 src/bingx/endpoints/bingx-historical-transaction-orders-endpoint.ts diff --git a/README.md b/README.md index 6dd246f..d713232 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ stream.latestTradeDetail$.subscribe((v) => {}) - [ ] User's Force Orders - [x] User's History Orders - [ ] Adjust isolated margin - - [ ] Query historical transaction orders + - [x] Query historical transaction orders * Listen Key - [x] Generate Listen Key - [ ] Extend Listen Key Validity period diff --git a/src/bingx-client/services/trade-historical-transaction-orders.service.spec.ts b/src/bingx-client/services/trade-historical-transaction-orders.service.spec.ts new file mode 100644 index 0000000..b8842d1 --- /dev/null +++ b/src/bingx-client/services/trade-historical-transaction-orders.service.spec.ts @@ -0,0 +1,91 @@ +import { TradeService } from 'bingx-api/bingx-client/services/trade.service'; +import { AccountInterface } from 'bingx-api/bingx/account/account.interface'; +import { RequestExecutorInterface } from 'bingx-api/bingx/request-executor/request-executor.interface'; +import { EndpointInterface } from 'bingx-api/bingx/endpoints/endpoint.interface'; +import { BingxHistoricalTransactionOrdersEndpoint } from 'bingx-api/bingx/endpoints/bingx-historical-transaction-orders-endpoint'; + +describe('trade historical transaction orders service', () => { + let account: AccountInterface; + let capturedEndpoints: EndpointInterface[]; + let requestExecutor: RequestExecutorInterface; + let executeSpy: jest.SpyInstance; + let nowSpy: jest.SpyInstance; + + beforeEach(() => { + account = { + getApiKey: jest.fn(() => 'api-key'), + sign: jest.fn(() => ({ + toString: () => 'signature', + secretKey: () => 'secret-key', + })), + }; + + capturedEndpoints = []; + requestExecutor = { + execute(endpoint: EndpointInterface): Promise { + capturedEndpoints.push(endpoint as EndpointInterface); + return Promise.resolve(endpoint as unknown as T); + }, + }; + + executeSpy = jest.spyOn(requestExecutor, 'execute'); + nowSpy = jest.spyOn(Date, 'now').mockReturnValue(1770000000123); + }); + + afterEach(() => { + nowSpy.mockRestore(); + }); + + it('dispatches the signed historical transaction orders endpoint', async () => { + const service = new TradeService(requestExecutor); + const startTs = new Date('2026-01-02T03:04:05.006Z'); + const endTs = 1770000000000; + + const endpoint = (await service.getHistoricalTransactionOrders( + { + symbol: 'WLD-USDT', + currency: 'USDT', + orderId: '1736007768311123456', + tradingUnit: 'COIN', + startTs, + endTs, + recvWindow: 5000, + }, + account, + )) as unknown as BingxHistoricalTransactionOrdersEndpoint; + + expect(executeSpy).toHaveBeenCalledTimes(1); + expect(capturedEndpoints[0]).toBe(endpoint); + expect(endpoint).toBeInstanceOf(BingxHistoricalTransactionOrdersEndpoint); + expect(endpoint.method()).toBe('get'); + expect(endpoint.path()).toBe('/openApi/swap/v2/trade/allFillOrders'); + expect(endpoint.parameters().asRecord()).toEqual({ + tradingUnit: 'COIN', + startTs: startTs.getTime().toString(10), + endTs: endTs.toString(10), + symbol: 'WLD-USDT', + currency: 'USDT', + orderId: '1736007768311123456', + recvWindow: '5000', + timestamp: '1770000000123', + }); + }); + + it('omits optional filters when only required history options are provided', () => { + const endpoint = new BingxHistoricalTransactionOrdersEndpoint( + { + tradingUnit: 'CONT', + startTs: 1770000000000, + endTs: 1770000001000, + }, + account, + ); + + expect(endpoint.parameters().asRecord()).toEqual({ + tradingUnit: 'CONT', + startTs: '1770000000000', + endTs: '1770000001000', + timestamp: '1770000000123', + }); + }); +}); diff --git a/src/bingx-client/services/trade.service.ts b/src/bingx-client/services/trade.service.ts index 65fa5bd..46b0322 100644 --- a/src/bingx-client/services/trade.service.ts +++ b/src/bingx-client/services/trade.service.ts @@ -12,6 +12,10 @@ import { BingxSwitchLeverageEndpoint } from 'bingx-api/bingx/endpoints/bingx-swi import { OrderPositionSideEnum } from 'bingx-api/bingx'; import { BingxUserHistoryOrdersEndpoint } from 'bingx-api/bingx/endpoints/bingx-user-history-orders-endpoint'; import { BingxCancelOrderEndpoint } from 'bingx-api/bingx/endpoints/bingx-cancel-order-endpoint'; +import { + BingxHistoricalTransactionOrdersEndpoint, + BingxHistoricalTransactionOrdersOptions, +} from 'bingx-api/bingx/endpoints/bingx-historical-transaction-orders-endpoint'; export class TradeService { constructor(private readonly requestExecutor: RequestExecutorInterface) {} @@ -62,6 +66,15 @@ export class TradeService { ); } + public async getHistoricalTransactionOrders( + options: BingxHistoricalTransactionOrdersOptions, + account: AccountInterface, + ) { + return this.requestExecutor.execute( + new BingxHistoricalTransactionOrdersEndpoint(options, account), + ); + } + public closeAllPositions(account: AccountInterface) { return this.requestExecutor.execute( new BingxCloseAllPositionsEndpoint(account), diff --git a/src/bingx/endpoints/bingx-historical-transaction-orders-endpoint.ts b/src/bingx/endpoints/bingx-historical-transaction-orders-endpoint.ts new file mode 100644 index 0000000..c9a5005 --- /dev/null +++ b/src/bingx/endpoints/bingx-historical-transaction-orders-endpoint.ts @@ -0,0 +1,100 @@ +import { + AccountInterface, + BingxResponse, + DefaultSignatureParameters, + EndpointInterface, + SignatureParametersInterface, +} from 'bingx-api/bingx'; +import { Endpoint } from 'bingx-api/bingx/endpoints/endpoint'; + +export type TradingUnit = 'COIN' | 'CONT'; + +export interface BingxHistoricalTransactionOrdersOptions { + tradingUnit: TradingUnit; + startTs: Date | number; + endTs: Date | number; + symbol?: string; + currency?: string; + orderId?: string | number; + recvWindow?: string | number; +} + +export interface BingxHistoricalTransactionOrder { + filledTm: string; + symbol: string; + volume: string; + price: string; + amount: string; + commission: string; + currency: string; + orderId: string; + liquidatedPrice: string; + liquidatedMarginRatio: string; + filledTime: string; + workingType?: string; + side?: string; + type?: string; + positionSide?: string; + clientOrderID?: string; + onlyOnePosition?: boolean; +} + +export interface BingxHistoricalTransactionOrdersData { + fill_orders: BingxHistoricalTransactionOrder[]; +} + +export class BingxHistoricalTransactionOrdersEndpoint< + R = BingxHistoricalTransactionOrdersData, + > + extends Endpoint> + implements EndpointInterface> +{ + constructor( + private readonly options: BingxHistoricalTransactionOrdersOptions, + account: AccountInterface, + ) { + super(account); + } + + method(): 'get' | 'post' | 'put' | 'patch' | 'delete' { + return 'get'; + } + + parameters(): SignatureParametersInterface { + const parameters: Record = { + tradingUnit: this.options.tradingUnit, + startTs: this.timestampAsString(this.options.startTs), + endTs: this.timestampAsString(this.options.endTs), + }; + + if (this.options.symbol !== undefined) { + parameters.symbol = this.options.symbol; + } + + if (this.options.currency !== undefined) { + parameters.currency = this.options.currency; + } + + if (this.options.orderId !== undefined) { + parameters.orderId = this.options.orderId.toString(); + } + + if (this.options.recvWindow !== undefined) { + parameters.recvWindow = this.options.recvWindow.toString(); + } + + return new DefaultSignatureParameters(parameters); + } + + path(): string { + return '/openApi/swap/v2/trade/allFillOrders'; + } + + private timestampAsString(value: Date | number): string { + return value instanceof Date + ? value.getTime().toString(10) + : value.toString(); + } + + readonly t!: BingxResponse; +} diff --git a/src/bingx/endpoints/index.ts b/src/bingx/endpoints/index.ts index 6866f8b..e9585fa 100644 --- a/src/bingx/endpoints/index.ts +++ b/src/bingx/endpoints/index.ts @@ -4,6 +4,7 @@ export * from './bingx-generate-listen-key-endpoint'; export * from './bingx-generate-listen-key-response'; export * from './bingx-get-perpetual-swap-account-asset-endpoint'; export * from './bingx-get-server-time-endpoint'; +export * from './bingx-historical-transaction-orders-endpoint'; export * from './bingx-perpetual-swap-positions-endpoint'; export * from './bingx-request.interface'; export * from './bingx-response.interface';