Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<unknown>[];
let requestExecutor: RequestExecutorInterface;
let executeSpy: jest.SpyInstance;
let nowSpy: jest.SpyInstance<number, []>;

beforeEach(() => {
account = {
getApiKey: jest.fn(() => 'api-key'),
sign: jest.fn(() => ({
toString: () => 'signature',
secretKey: () => 'secret-key',
})),
};

capturedEndpoints = [];
requestExecutor = {
execute<T>(endpoint: EndpointInterface<T>): Promise<T> {
capturedEndpoints.push(endpoint as EndpointInterface<unknown>);
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',
});
});
});
13 changes: 13 additions & 0 deletions src/bingx-client/services/trade.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down Expand Up @@ -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),
Expand Down
100 changes: 100 additions & 0 deletions src/bingx/endpoints/bingx-historical-transaction-orders-endpoint.ts
Original file line number Diff line number Diff line change
@@ -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<BingxResponse<R>>
implements EndpointInterface<BingxResponse<R>>
{
constructor(
private readonly options: BingxHistoricalTransactionOrdersOptions,
account: AccountInterface,
) {
super(account);
}

method(): 'get' | 'post' | 'put' | 'patch' | 'delete' {
return 'get';
}

parameters(): SignatureParametersInterface {
const parameters: Record<string, string> = {
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<R>;
}
1 change: 1 addition & 0 deletions src/bingx/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down