From f9774ea916bc05e66beb60403575a57011ae4971 Mon Sep 17 00:00:00 2001 From: "ilinikh.a1" Date: Thu, 1 Feb 2024 21:27:25 +0600 Subject: [PATCH] fix: parsing big int in json #68 --- package-lock.json | 17 +++++ package.json | 1 + ...bingx-api-client.create-test-order.spec.ts | 43 +++++++++++++ src/bingx-client/services/trade.service.ts | 9 +++ .../bingx-account-socket-stream.spec.ts | 64 ++++++++++++++++++- .../bingx-websocket-deserializer.ts | 3 +- .../events/account-websocket-events.ts | 17 ++--- src/bingx/endpoints/bingx-request.ts | 9 +++ .../bingx-trade-order-test-endpoint.ts | 48 ++++++++++++++ src/json-bignumber.ts | 3 + 10 files changed, 203 insertions(+), 11 deletions(-) create mode 100644 src/bingx-client/playground/bingx-api-client.create-test-order.spec.ts create mode 100644 src/bingx/endpoints/bingx-trade-order-test-endpoint.ts create mode 100644 src/json-bignumber.ts diff --git a/package-lock.json b/package-lock.json index 43a5096..7959abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "axios": "^1.5.1", "axios-request-throttle": "^1.0.0", "date-fns": "^3.3.1", + "json-bignumber": "^1.1.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "ws": "^8.14.2" @@ -3628,6 +3629,14 @@ "node": ">=0.6" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -7254,6 +7263,14 @@ "node": ">=4" } }, + "node_modules/json-bignumber": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-bignumber/-/json-bignumber-1.1.1.tgz", + "integrity": "sha512-RWC/dAB/vOVosewwvPIfWgI/061fXZ0PbZxXr8GJvw8sDr8OzDm8vt7CZUdelg3QtKRdH4TUjnb++CXlRFRnPg==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", diff --git a/package.json b/package.json index 3d5ccc8..423809a 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "axios": "^1.5.1", "axios-request-throttle": "^1.0.0", "date-fns": "^3.3.1", + "json-bignumber": "^1.1.1", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "ws": "^8.14.2" diff --git a/src/bingx-client/playground/bingx-api-client.create-test-order.spec.ts b/src/bingx-client/playground/bingx-api-client.create-test-order.spec.ts new file mode 100644 index 0000000..0822811 --- /dev/null +++ b/src/bingx-client/playground/bingx-api-client.create-test-order.spec.ts @@ -0,0 +1,43 @@ +import { EnvProviderService } from '@ledius/env'; +import { + ApiAccount, + HttpRequestExecutor, + OrderPositionSideEnum, + OrderSideEnum, + OrderTypeEnum, +} from 'bingx-api/bingx'; +import { BingxApiClient } from 'bingx-api/bingx-client'; + +describe('create test order', () => { + const env = new EnvProviderService(); + + const account = new ApiAccount( + env.getOrFail('API_ACCOUNT_KEY'), + env.getOrFail('API_ACCOUNT_SECRET'), + ); + + let client: BingxApiClient; + + beforeAll(() => { + client = new BingxApiClient(new HttpRequestExecutor()); + }); + + describe('create test order', () => { + it('should be create order', async () => { + const response = await client.getTradeService().createTradeOrderTest( + { + clientOrderID: '', + side: OrderSideEnum.BUY, + positionSide: OrderPositionSideEnum.LONG, + price: '30000', + quantity: '0.001', + symbol: 'BTC-USDT', + type: OrderTypeEnum.MARKET, + }, + account, + ); + + expect(response).toStrictEqual({}); + }); + }); +}); diff --git a/src/bingx-client/services/trade.service.ts b/src/bingx-client/services/trade.service.ts index e215bb6..3712a45 100644 --- a/src/bingx-client/services/trade.service.ts +++ b/src/bingx-client/services/trade.service.ts @@ -24,6 +24,15 @@ export class TradeService { ); } + public async createTradeOrderTest( + order: BingxCreateTradeOrderInterface, + account: AccountInterface, + ) { + return this.requestExecutor.execute( + new BingxTradeOrderEndpoint(order, account), + ); + } + public async getUserHistoryOrders( symbol: string, limit: number, diff --git a/src/bingx-socket/bingx-account-socket-stream.spec.ts b/src/bingx-socket/bingx-account-socket-stream.spec.ts index 895f6af..1b5e2ed 100644 --- a/src/bingx-socket/bingx-account-socket-stream.spec.ts +++ b/src/bingx-socket/bingx-account-socket-stream.spec.ts @@ -179,7 +179,7 @@ describe('bingx account socket stream', () => { expect(e).toStrictEqual({ e: AccountWebsocketEventType.ORDER_TRADE_UPDATE, o: {}, - E: 111, + E: '111', } as AccountOrderUpdatePushEvent); done(); }); @@ -189,10 +189,70 @@ describe('bingx account socket stream', () => { JSON.stringify({ e: AccountWebsocketEventType.ORDER_TRADE_UPDATE, o: {}, - E: 111, + E: '111', } as AccountOrderUpdatePushEvent), ); }, 1000); }); }); + + describe('#68 fix big number in order identifier', () => { + const requestExecutorMock = { + execute: jest.fn().mockResolvedValueOnce({ data: { listenKey: '123' } }), + }; + let stream: BingxAccountSocketStream; + beforeAll(() => { + const account = new ApiAccount('xxx', 'xxx'); + stream = new BingxAccountSocketStream(account, { + requestExecutor: requestExecutorMock, + url: new URL('', `ws://0.0.0.0:${port}`), + }); + }); + + afterAll(() => { + stream.disconnect(); + }); + + it('must be got listen key', () => { + expect(requestExecutorMock.execute).toHaveBeenCalledTimes(1); + }); + + it('must be got order push', (done) => { + stream.accountOrderUpdatePushEvent$.subscribe((e) => { + expect(e).toStrictEqual({ + e: AccountWebsocketEventType.ORDER_TRADE_UPDATE, + o: { + N: 'USDT', + S: 'BUY', + T: '0', + X: 'NEW', + c: '', + i: '172998235239792314304', + n: '0.00000000', + o: 'TRIGGER_MARKET', + p: '', + q: '0.00100000', + s: 'BTC-USDT', + x: 'TRADE', + z: '0.00000000', + ap: '0.00000000', + ps: 'LONG', + rp: '0.00000000', + sp: '43495.00000000', + wt: 'MARK_PRICE', + }, + E: '1706736600541', + } as AccountOrderUpdatePushEvent); + done(); + }); + setTimeout(() => { + sendToSocket( + sockets[0], + '{"E":1706736600541,"e":"ORDER_TRADE_UPDATE","o":{"N":"USDT","S":"BUY","T":0,"X":"NEW","c":"","i":172998235239792314304,' + + '"n":"0.00000000","o":"TRIGGER_MARKET","p":"","q":"0.00100000","s":"BTC-USDT",' + + '"x":"TRADE","z":"0.00000000","ap":"0.00000000","ps":"LONG","rp":"0.00000000","sp":"43495.00000000","wt":"MARK_PRICE"}}', + ); + }, 1000); + }); + }); }); diff --git a/src/bingx-socket/bingx-websocket-deserializer.ts b/src/bingx-socket/bingx-websocket-deserializer.ts index f5de9ac..6df19ea 100644 --- a/src/bingx-socket/bingx-websocket-deserializer.ts +++ b/src/bingx-socket/bingx-websocket-deserializer.ts @@ -1,5 +1,6 @@ import { WebSocketSubjectConfig } from 'rxjs/internal/observable/dom/WebSocketSubject'; import * as zlib from 'zlib'; +import * as JSONBigNumber from 'json-bignumber'; export class BingxWebsocketDeserializer implements Required, 'deserializer'>> @@ -8,7 +9,7 @@ export class BingxWebsocketDeserializer const message = zlib.unzipSync(e.data).toString('utf-8'); try { - return JSON.parse(message); + return JSON.parse(JSON.stringify(JSONBigNumber.parse(message))); } catch (e) { return message; } diff --git a/src/bingx-socket/events/account-websocket-events.ts b/src/bingx-socket/events/account-websocket-events.ts index a5f1057..b42b8ef 100644 --- a/src/bingx-socket/events/account-websocket-events.ts +++ b/src/bingx-socket/events/account-websocket-events.ts @@ -20,6 +20,7 @@ export enum WebSocketOrderType { STOP = 'STOP', TAKE_PROFIT = 'TAKE_PROFIT', LIQUIDATION = 'LIQUIDATION', + TRIGGER_MARKET = 'TRIGGER_MARKET', } export enum WebSocketOrderExecutionType { @@ -44,7 +45,7 @@ export enum WebSocketTriggerPriceType { INDEX_PRICE = 'INDEX_PRICE', } -export type EventTimeInMilliseconds = number; +export type EventTimeInMilliseconds = string; /** * Relates account events @@ -65,13 +66,13 @@ export type IsolatedPositionMargin = number; */ export type ClientCustomOrderId = string; export type OrderId = string; -export type Quantity = number; -export type Price = number; -export type AveragePrice = number; -export type HandlingFee = number; -export type TransactionTime = number; -export type TransactionAchievedProfitAndLoss = number; -export type OrderFilledAccumulatedQuantity = number; +export type Quantity = string; +export type Price = string; +export type AveragePrice = string; +export type HandlingFee = string; +export type TransactionTime = string; +export type TransactionAchievedProfitAndLoss = string; +export type OrderFilledAccumulatedQuantity = string; export type FeeAssetType = string; export interface AccountBalanceInformation { diff --git a/src/bingx/endpoints/bingx-request.ts b/src/bingx/endpoints/bingx-request.ts index 0a4cf25..bbad69f 100644 --- a/src/bingx/endpoints/bingx-request.ts +++ b/src/bingx/endpoints/bingx-request.ts @@ -3,6 +3,7 @@ import { lastValueFrom } from 'rxjs'; import { BingxRequestInterface } from 'bingx-api/bingx/endpoints/bingx-request.interface'; import { HttpService } from '@nestjs/axios'; import axios from 'axios'; +import * as JSONBigNumber from 'json-bignumber'; export class BingxRequest implements BingxRequestInterface { private readonly http = new HttpService( @@ -11,6 +12,14 @@ export class BingxRequest implements BingxRequestInterface { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, + transformResponse: (res) => { + try { + return JSON.parse(JSON.stringify(JSONBigNumber.parse(res))); + } catch (e) { + console.error(e); + return res; + } + }, }), ); diff --git a/src/bingx/endpoints/bingx-trade-order-test-endpoint.ts b/src/bingx/endpoints/bingx-trade-order-test-endpoint.ts new file mode 100644 index 0000000..d57d15e --- /dev/null +++ b/src/bingx/endpoints/bingx-trade-order-test-endpoint.ts @@ -0,0 +1,48 @@ +import { EndpointInterface } from 'bingx-api/bingx/endpoints/endpoint.interface'; +import { Endpoint } from 'bingx-api/bingx/endpoints/endpoint'; +import { BingxResponse, SignatureParametersInterface } from 'bingx-api/bingx'; +import { AccountInterface } from 'bingx-api/bingx/account/account.interface'; +import { BingxCreateTradeOrderInterface } from 'bingx-api/bingx/interfaces/trade-order.interface'; +import { DefaultSignatureParameters } from 'bingx-api/bingx/account/default-signature-parameters'; +import { OrderSideEnum } from 'bingx-api/bingx/enums/order-side.enum'; +import { OrderTypeEnum } from 'bingx-api/bingx/enums/order-type.enum'; +import { OrderPositionSideEnum } from 'bingx-api/bingx/enums/order-position-side.enum'; + +export interface BingxOrderResponseInterface { + order: { + symbol: string; + side: OrderSideEnum; + type: OrderTypeEnum; + positionSide: OrderPositionSideEnum; + orderId: number; + clientOrderId: string; + }; +} + +export class BingxTradeOrderTestEndpoint + extends Endpoint + implements EndpointInterface> +{ + constructor( + private readonly order: BingxCreateTradeOrderInterface, + account: AccountInterface, + ) { + super(account); + } + + readonly t!: BingxResponse; + + method(): 'get' | 'post' | 'put' | 'patch' | 'delete' { + return 'post'; + } + + parameters(): SignatureParametersInterface { + return new DefaultSignatureParameters({ + ...this.order, + }); + } + + path(): string { + return '/openApi/swap/v2/trade/order/test'; + } +} diff --git a/src/json-bignumber.ts b/src/json-bignumber.ts new file mode 100644 index 0000000..6b80306 --- /dev/null +++ b/src/json-bignumber.ts @@ -0,0 +1,3 @@ +declare module 'json-bignumber' { + function parse(input: string): Record; +}