futureConnector.performance.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. import { FutureConnector } from '../../src/exchanges/binance/FutureConnector'
  2. import { TestHelpers } from '../utils/testHelpers'
  3. const describeBinancePerf = process.env.TEST_BINANCE === '1' ? describe : describe.skip
  4. // Mock Binance API client
  5. jest.mock('@binance/derivatives-trading-usds-futures', () => ({
  6. DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL: 'https://fapi.binance.com',
  7. DerivativesTradingUsdsFutures: jest.fn(),
  8. DerivativesTradingUsdsFuturesRestAPI: {
  9. NewOrderSideEnum: {
  10. BUY: 'BUY',
  11. SELL: 'SELL',
  12. },
  13. NewOrderPositionSideEnum: {
  14. LONG: 'LONG',
  15. SHORT: 'SHORT',
  16. BOTH: 'BOTH',
  17. },
  18. NewOrderTimeInForceEnum: {
  19. GTC: 'GTC',
  20. IOC: 'IOC',
  21. FOK: 'FOK',
  22. GTX: 'GTX',
  23. GTD: 'GTD',
  24. },
  25. NewOrderNewOrderRespTypeEnum: {
  26. ACK: 'ACK',
  27. RESULT: 'RESULT',
  28. FULL: 'FULL',
  29. },
  30. ChangeMarginTypeMarginTypeEnum: {
  31. ISOLATED: 'ISOLATED',
  32. CROSSED: 'CROSSED',
  33. },
  34. ModifyOrderSideEnum: {
  35. BUY: 'BUY',
  36. SELL: 'SELL',
  37. },
  38. },
  39. }))
  40. // 性能测试
  41. describeBinancePerf('FutureConnector Performance Tests', () => {
  42. let connector: FutureConnector
  43. let mockClient: any
  44. beforeEach(() => {
  45. // 创建模拟的 API 客户端
  46. mockClient = {
  47. restAPI: {
  48. accountInformationV3: jest.fn(),
  49. newOrder: jest.fn(),
  50. currentAllOpenOrders: jest.fn(),
  51. allOrders: jest.fn(),
  52. accountTradeList: jest.fn(),
  53. },
  54. }
  55. // Mock 构造函数
  56. const { DerivativesTradingUsdsFutures } = require('@binance/derivatives-trading-usds-futures')
  57. DerivativesTradingUsdsFutures.mockImplementation(() => mockClient)
  58. connector = new FutureConnector('test-api-key', 'test-api-secret')
  59. })
  60. afterEach(() => {
  61. jest.clearAllMocks()
  62. })
  63. describe('API Response Time', () => {
  64. test('should handle fast API responses', async () => {
  65. const startTime = Date.now()
  66. mockClient.restAPI.accountInformationV3.mockResolvedValue({
  67. data: jest.fn().mockResolvedValue({
  68. assets: TestHelpers.createMockAssets(),
  69. positions: TestHelpers.createMockPositions(),
  70. }),
  71. })
  72. const result = await connector.getAssetsInfo()
  73. const endTime = Date.now()
  74. const responseTime = endTime - startTime
  75. expect(result).toBeDefined()
  76. expect(responseTime).toBeLessThan(1000) // 应该在1秒内完成
  77. })
  78. test('should handle slow API responses gracefully', async () => {
  79. const startTime = Date.now()
  80. // 模拟慢速 API 响应
  81. mockClient.restAPI.accountInformationV3.mockImplementation(() => {
  82. return new Promise(resolve => {
  83. setTimeout(() => {
  84. resolve({
  85. data: jest.fn().mockResolvedValue({
  86. assets: TestHelpers.createMockAssets(),
  87. positions: TestHelpers.createMockPositions(),
  88. }),
  89. })
  90. }, 2000) // 2秒延迟
  91. })
  92. })
  93. const result = await connector.getAssetsInfo()
  94. const endTime = Date.now()
  95. const responseTime = endTime - startTime
  96. expect(result).toBeDefined()
  97. expect(responseTime).toBeGreaterThanOrEqual(2000)
  98. })
  99. })
  100. describe('Concurrent API Calls', () => {
  101. test('should handle multiple concurrent getAssetsInfo calls', async () => {
  102. const mockData = {
  103. assets: TestHelpers.createMockAssets(),
  104. positions: TestHelpers.createMockPositions(),
  105. }
  106. mockClient.restAPI.accountInformationV3.mockResolvedValue({
  107. data: jest.fn().mockResolvedValue(mockData),
  108. })
  109. const startTime = Date.now()
  110. // 并发调用 10 次
  111. const promises = Array.from({ length: 10 }, () => connector.getAssetsInfo())
  112. const results = await Promise.all(promises)
  113. const endTime = Date.now()
  114. const totalTime = endTime - startTime
  115. expect(results).toHaveLength(10)
  116. results.forEach(result => {
  117. expect(result).toEqual(mockData.assets.filter(asset => Number(asset.walletBalance) > 0))
  118. })
  119. // 验证 API 被调用了 10 次
  120. expect(mockClient.restAPI.accountInformationV3).toHaveBeenCalledTimes(10)
  121. console.log(`并发调用 10 次 getAssetsInfo 总耗时: ${totalTime}ms`)
  122. })
  123. test('should handle concurrent order operations', async () => {
  124. const mockOrderResult = TestHelpers.createMockOrderResult(12345, 'NEW')
  125. mockClient.restAPI.newOrder.mockResolvedValue({
  126. data: jest.fn().mockResolvedValue(mockOrderResult),
  127. })
  128. const startTime = Date.now()
  129. // 并发创建 5 个订单
  130. const promises = Array.from({ length: 5 }, (_, index) =>
  131. connector.openLimitOrder(
  132. 'BTCUSDT',
  133. 'long',
  134. 0.001,
  135. 50000 + index, // 不同的价格
  136. 'GTC',
  137. ),
  138. )
  139. const results = await Promise.all(promises)
  140. const endTime = Date.now()
  141. const totalTime = endTime - startTime
  142. expect(results).toHaveLength(5)
  143. results.forEach(result => {
  144. expect(result).toEqual(mockOrderResult)
  145. })
  146. expect(mockClient.restAPI.newOrder).toHaveBeenCalledTimes(5)
  147. console.log(`并发创建 5 个订单总耗时: ${totalTime}ms`)
  148. })
  149. })
  150. describe('Memory Usage', () => {
  151. test('should not leak memory with repeated API calls', async () => {
  152. const initialMemory = process.memoryUsage()
  153. mockClient.restAPI.accountInformationV3.mockResolvedValue({
  154. data: jest.fn().mockResolvedValue({
  155. assets: TestHelpers.createMockAssets(),
  156. positions: TestHelpers.createMockPositions(),
  157. }),
  158. })
  159. // 执行 100 次 API 调用
  160. for (let i = 0; i < 100; i++) {
  161. await connector.getAssetsInfo()
  162. }
  163. const finalMemory = process.memoryUsage()
  164. const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed
  165. // 内存增长应该在合理范围内(小于 10MB)
  166. expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024)
  167. console.log(`100 次 API 调用内存增长: ${(memoryIncrease / 1024 / 1024).toFixed(2)}MB`)
  168. })
  169. })
  170. describe('Error Recovery', () => {
  171. test('should handle API errors without performance degradation', async () => {
  172. let callCount = 0
  173. mockClient.restAPI.accountInformationV3.mockImplementation(() => {
  174. callCount++
  175. if (callCount <= 3) {
  176. // 前 3 次调用失败
  177. return Promise.reject(new Error('API Error'))
  178. } else {
  179. // 后续调用成功
  180. return Promise.resolve({
  181. data: jest.fn().mockResolvedValue({
  182. assets: TestHelpers.createMockAssets(),
  183. positions: TestHelpers.createMockPositions(),
  184. }),
  185. })
  186. }
  187. })
  188. const startTime = Date.now()
  189. // 重试机制应该能够处理错误
  190. const result = await TestHelpers.retry(
  191. () => connector.getAssetsInfo(),
  192. 5, // 最多重试 5 次
  193. 100, // 每次重试间隔 100ms
  194. )
  195. const endTime = Date.now()
  196. const totalTime = endTime - startTime
  197. expect(result).toBeDefined()
  198. expect(callCount).toBeGreaterThan(3)
  199. console.log(`错误恢复测试总耗时: ${totalTime}ms, API 调用次数: ${callCount}`)
  200. })
  201. })
  202. describe('Large Data Handling', () => {
  203. test('should handle large position data efficiently', async () => {
  204. // 创建大量持仓数据
  205. const largePositions = Array.from({ length: 1000 }, (_, index) => ({
  206. symbol: `SYMBOL${index}USDT`,
  207. positionAmt: (Math.random() * 100).toString(),
  208. unrealizedPnl: (Math.random() * 1000 - 500).toString(),
  209. entryPrice: (Math.random() * 100000).toString(),
  210. leverage: '10',
  211. isolated: false,
  212. initialMargin: '0.1',
  213. maintMargin: '0.1',
  214. positionInitialMargin: '0.1',
  215. openOrderInitialMargin: '0.0',
  216. maxNotional: '1000000',
  217. bidNotional: '0',
  218. askNotional: '0',
  219. positionSide: 'BOTH',
  220. updateTime: Date.now(),
  221. }))
  222. mockClient.restAPI.accountInformationV3.mockResolvedValue({
  223. data: jest.fn().mockResolvedValue({
  224. assets: TestHelpers.createMockAssets(),
  225. positions: largePositions,
  226. }),
  227. })
  228. const startTime = Date.now()
  229. const result = await connector.getPositonInfo()
  230. const endTime = Date.now()
  231. const processingTime = endTime - startTime
  232. expect(result).toBeDefined()
  233. expect(Array.isArray(result)).toBe(true)
  234. // 处理 1000 条数据应该在合理时间内完成
  235. expect(processingTime).toBeLessThan(5000) // 5秒内
  236. console.log(`处理 1000 条持仓数据耗时: ${processingTime}ms`)
  237. })
  238. test('should handle large order history efficiently', async () => {
  239. // 创建大量订单数据
  240. const largeOrders = Array.from({ length: 500 }, (_, index) => ({
  241. orderId: 10000 + index,
  242. symbol: 'BTCUSDT',
  243. status: index % 2 === 0 ? 'FILLED' : 'CANCELED',
  244. clientOrderId: `test_order_${index}`,
  245. price: (50000 + Math.random() * 1000).toString(),
  246. avgPrice: (50000 + Math.random() * 1000).toString(),
  247. origQty: (0.001 + Math.random() * 0.01).toString(),
  248. executedQty: (0.001 + Math.random() * 0.01).toString(),
  249. cumQuote: (50 + Math.random() * 100).toString(),
  250. timeInForce: 'GTC',
  251. type: index % 3 === 0 ? 'LIMIT' : 'MARKET',
  252. reduceOnly: false,
  253. closePosition: false,
  254. side: index % 2 === 0 ? 'BUY' : 'SELL',
  255. positionSide: 'LONG',
  256. stopPrice: '0',
  257. workingType: 'CONTRACT_PRICE',
  258. priceProtect: false,
  259. origType: index % 3 === 0 ? 'LIMIT' : 'MARKET',
  260. time: Date.now() - Math.random() * 86400000, // 过去24小时内
  261. updateTime: Date.now() - Math.random() * 86400000,
  262. }))
  263. mockClient.restAPI.allOrders.mockResolvedValue({
  264. data: jest.fn().mockResolvedValue(largeOrders),
  265. })
  266. const startTime = Date.now()
  267. const result = await connector.getOrderHistory('BTCUSDT')
  268. const endTime = Date.now()
  269. const processingTime = endTime - startTime
  270. expect(result).toBeDefined()
  271. expect(Array.isArray(result)).toBe(true)
  272. expect(result).toHaveLength(500)
  273. // 处理 500 条订单数据应该在合理时间内完成
  274. expect(processingTime).toBeLessThan(3000) // 3秒内
  275. console.log(`处理 500 条订单历史耗时: ${processingTime}ms`)
  276. })
  277. })
  278. describe('Rate Limiting Simulation', () => {
  279. test('should handle rate limiting gracefully', async () => {
  280. let callCount = 0
  281. const rateLimitDelay = 100 // 模拟 100ms 的速率限制
  282. mockClient.restAPI.accountInformationV3.mockImplementation(() => {
  283. callCount++
  284. return new Promise(resolve => {
  285. setTimeout(() => {
  286. resolve({
  287. data: jest.fn().mockResolvedValue({
  288. assets: TestHelpers.createMockAssets(),
  289. positions: TestHelpers.createMockPositions(),
  290. }),
  291. })
  292. }, rateLimitDelay)
  293. })
  294. })
  295. const startTime = Date.now()
  296. // 连续调用 10 次
  297. const promises = Array.from({ length: 10 }, () => connector.getAssetsInfo())
  298. const results = await Promise.all(promises)
  299. const endTime = Date.now()
  300. const totalTime = endTime - startTime
  301. expect(results).toHaveLength(10)
  302. // 由于速率限制,总时间应该接近 10 * 100ms = 1000ms
  303. expect(totalTime).toBeGreaterThanOrEqual(rateLimitDelay * 10 * 0.8) // 允许 20% 的误差
  304. console.log(`速率限制测试总耗时: ${totalTime}ms, 预期: ${rateLimitDelay * 10}ms`)
  305. })
  306. })
  307. })