@imbingox/acex 0.1.0-beta.3 → 0.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @imbingox/acex
2
2
 
3
- `acex` 是一个面向交易场景的状态型 SDK。调用方只需要持有一个 `AcexClient`,就可以通过统一的 `market`、`account`、`order` manager 读取最新快照、订阅增量事件、观察健康状态,并在当前 Binance MVP 范围内执行第一版下单/撤单命令,而不需要自己维护本地缓存、ready barrierwebsocket 生命周期。
3
+ `acex` 是一个面向交易场景的 **状态型** 多交易所 SDK。调用方持有一个 `AcexClient`,通过统一的 `market` / `account` / `order` manager 读取最新快照、消费增量事件、执行下单撤单命令;SDK 内部负责本地缓存、ready barrierwebsocket 生命周期和自动重连,调用方不需要自己处理。
4
+
5
+ 当前 MVP 只落地 Binance(Spot + USDⓈ-M + COIN-M 行情,PAPI UM 私有链路)。
4
6
 
5
7
  ## 安装
6
8
 
@@ -8,264 +10,56 @@
8
10
  bun add @imbingox/acex
9
11
  ```
10
12
 
11
- ## 完整用法
13
+ ## 快速上手
12
14
 
13
- ### 1. 创建 client
15
+ ### 行情(无需凭证)
14
16
 
15
17
  ```ts
16
18
  import { createClient } from "@imbingox/acex";
17
- // SDK re-exports BigNumber,调用方无需单独安装 bignumber.js
18
- import { BigNumber } from "@imbingox/acex";
19
19
 
20
20
  const client = createClient();
21
- ```
22
-
23
- `createClient` 接受一个可选的配置对象,当前真正生效的是 `market.*` 运行时参数:
24
-
25
- ```ts
26
- const client = createClient({
27
- market: {
28
- l1InitialMessageTimeoutMs: 15_000, // L1 Book 首条消息超时(默认 15s)
29
- l1StaleAfterMs: 15_000, // 多久没收到消息标记 stale(默认 15s)
30
- l1ReconnectDelayMs: 1_000, // 断线重连初始延迟
31
- l1ReconnectMaxDelayMs: 10_000, // 断线重连最大延迟(指数退避上限)
32
- },
33
- });
34
- ```
35
-
36
- > `sandbox`、`logger`、`logLevel` 已预留但当前未生效。
37
-
38
- ### 2. 生命周期
39
-
40
- ```ts
41
- // 启动 client(必须在所有 subscribe 之前)
42
21
  await client.start();
43
22
 
44
- // ... 使用 client ...
45
-
46
- // 停止 client(释放所有 websocket、订阅关系)
47
- await client.stop();
48
-
49
- // 也可以指定优雅退出选项
50
- await client.stop({ graceful: true, timeoutMs: 5000 });
51
- ```
52
-
53
- Client 的状态机:`idle` → `starting` → `running` → `stopping` → `stopped`。
54
-
55
- 可以通过 `client.getStatus()` 随时查看当前状态。
56
-
57
- ### 3. Market Catalog(市场列表)
58
-
59
- 加载市场列表后可以发现可用交易对、读取精度参数:
60
-
61
- ```ts
62
- // 拉取并缓存所有交易所的 market catalog
63
- await client.market.loadMarkets();
64
-
65
- // 列出所有 market
66
- const markets = client.market.listMarkets();
67
- // → MarketDefinition[]
68
-
69
- // 只列出指定交易所的 market
70
- const binanceMarkets = client.market.listMarkets("binance");
71
-
72
- // 按交易所 + 统一 symbol 查询单个 market
73
- const btcPerp = client.market.getMarket("binance", "BTC/USDT:USDT");
74
- // → MarketDefinition | undefined
75
-
76
- // 查询一个 symbol 在所有交易所的 market(多交易所场景)
77
- const allBtcPerp = client.market.findMarkets("BTC/USDT:USDT");
78
- // → MarketDefinition[]
79
- ```
80
-
81
- 返回的 `MarketDefinition` 包含以下字段:
82
-
83
- ```ts
84
- {
85
- exchange: "binance",
86
- symbol: "BTC/USDT:USDT", // 统一 symbol
87
- id: "BTCUSDT", // 交易所原始 symbol
88
- type: "swap", // "spot" | "swap" | "future"
89
- base: "BTC",
90
- quote: "USDT",
91
- settle: "USDT", // 结算币种(swap/future 才有)
92
- active: true, // 是否可交易
93
- contract: true, // 是否合约
94
- linear: true, // U 本位
95
- inverse: false, // 币本位
96
- contractSize: BigNumber(1),
97
- pricePrecision: 1,
98
- amountPrecision: 3,
99
- priceStep: BigNumber(0.10), // 最小价格变动
100
- amountStep: BigNumber(0.001), // 最小数量变动
101
- minNotional: BigNumber(5), // 最小名义价值
102
- raw: { ... }, // 交易所原始数据
103
- }
104
- ```
105
-
106
- > 所有价格、数量、金额字段均为 `BigNumber` 类型(来自 [bignumber.js](https://github.com/MikeMcl/bignumber.js)),可直接进行算术运算。
107
-
108
- **统一 symbol 约定:**
109
-
110
- | symbol 格式 | 含义 | 示例 |
111
- |---|---|---|
112
- | `BASE/QUOTE` | spot 现货 | `BTC/USDT` |
113
- | `BASE/QUOTE:SETTLE` | USDⓈ-M 永续 | `BTC/USDT:USDT` |
114
- | `BASE/USD:BASE` | COIN-M 永续 | `BTC/USD:BTC` |
115
- | `BASE/USD:BASE-YYYYMMDD` | COIN-M 交割 | `BTC/USD:BTC-20250627` |
116
-
117
- ### 4. L1 Book(最优买卖价)
118
-
119
- 这是当前最核心的实时数据能力。
120
-
121
- #### 订阅
122
-
123
- ```ts
124
- // subscribeL1Book 是 ready barrier:
125
- // await 返回后,getL1Book 已经可以拿到首个可用快照
126
23
  await client.market.subscribeL1Book({
127
24
  exchange: "binance",
128
25
  symbol: "BTC/USDT:USDT",
129
26
  });
130
- ```
131
27
 
132
- `subscribeL1Book` 会自动确保 market catalog 已加载,所以不必手动先调 `loadMarkets()`。
133
-
134
- #### 读取快照
135
-
136
- ```ts
137
28
  const book = client.market.getL1Book({
138
29
  exchange: "binance",
139
30
  symbol: "BTC/USDT:USDT",
140
31
  });
32
+ console.log(`bid=${book?.bidPrice.toFixed()} ask=${book?.askPrice.toFixed()}`);
33
+ console.log(`book freshness=${book?.status.freshness}`);
141
34
 
142
- if (book) {
143
- console.log(book.bidPrice, book.bidSize); // 最优买(BigNumber)
144
- console.log(book.askPrice, book.askSize); // 最优卖(BigNumber)
145
- // 直接算术运算
146
- const spread = book.askPrice.minus(book.bidPrice);
147
- console.log(`spread: ${spread.toFixed()}`);
148
- }
149
- ```
150
-
151
- `L1Book` 完整结构:
152
-
153
- ```ts
154
- {
35
+ await client.market.subscribeFundingRate({
155
36
  exchange: "binance",
156
37
  symbol: "BTC/USDT:USDT",
157
- bidPrice: BigNumber("104321.50"),
158
- bidSize: BigNumber("1.234"),
159
- askPrice: BigNumber("104321.60"),
160
- askSize: BigNumber("0.567"),
161
- exchangeTs: 1710000000000, // 交易所时间戳(可能为空)
162
- receivedAt: 1710000000001, // SDK 收到时间
163
- updatedAt: 1710000000001, // SDK 更新时间
164
- version: 42, // 递增序号
165
- }
166
- ```
167
-
168
- > 所有价格和数量都是 `BigNumber` 类型,避免浮点精度问题,可直接进行算术运算。
38
+ });
169
39
 
170
- #### 消费增量事件
40
+ const funding = client.market.getFundingRate({
41
+ exchange: "binance",
42
+ symbol: "BTC/USDT:USDT",
43
+ });
44
+ console.log(`funding=${funding?.fundingRate.toFixed()}`);
171
45
 
172
- ```ts
173
- // events.* 只消费事件,不会隐式触发订阅
174
- // 必须先 subscribeL1Book 才会有数据流
175
46
  for await (const event of client.market.events.l1BookUpdates({
176
47
  exchange: "binance",
177
48
  symbol: "BTC/USDT:USDT",
178
49
  })) {
179
- console.log(event.snapshot.bidPrice, event.snapshot.askPrice);
180
- }
181
- ```
182
-
183
- 也可以手动控制迭代器:
184
-
185
- ```ts
186
- const iterator = client.market.events
187
- .l1BookUpdates({ exchange: "binance", symbol: "BTC/USDT:USDT" })
188
- [Symbol.asyncIterator]();
189
-
190
- const { value, done } = await iterator.next();
191
- if (!done) {
192
- console.log(value.snapshot);
193
- }
194
-
195
- // 不再消费时,释放迭代器
196
- await iterator.return?.();
197
- ```
198
-
199
- 不传 filter 可以接收所有 symbol 的更新:
200
-
201
- ```ts
202
- for await (const event of client.market.events.l1BookUpdates()) {
203
- console.log(event.exchange, event.symbol, event.snapshot.bidPrice);
204
- }
205
- ```
206
-
207
- #### 事件当触发器(推荐模式)
208
-
209
- `event.snapshot` 是事件发生那一刻的快照,但由于事件异步消费,处理时内部状态可能已被更新。如果你需要同时读取多个 symbol 的最新价格(如套利、对冲),推荐把事件当触发器,用 `getL1Book()` 读最新值:
210
-
211
- ```ts
212
- const pairs = [
213
- { exchange: "binance", symbol: "BTC/USDT:USDT" },
214
- { exchange: "binance", symbol: "BTC/USD:BTC" },
215
- ];
216
-
217
- for (const pair of pairs) {
218
- await client.market.subscribeL1Book(pair);
50
+ console.log(event.snapshot.bidPrice.toFixed());
51
+ break;
219
52
  }
220
53
 
221
- // 不带 filter — 任何 symbol 变动都触发
222
- for await (const event of client.market.events.l1BookUpdates()) {
223
- const books = pairs.map((pair) => ({
224
- ...pair,
225
- book: client.market.getL1Book(pair),
226
- }));
227
-
228
- if (books.some((b) => !b.book)) continue;
229
-
230
- // 所有 symbol 的最新价格,执行你的策略逻辑
231
- doSomething(books);
232
- }
233
- ```
234
-
235
- #### 查看订阅状态
236
-
237
- ```ts
238
- const status = client.market.getMarketStatus({
239
- exchange: "binance",
240
- symbol: "BTC/USDT:USDT",
241
- });
242
-
243
- if (status) {
244
- status.activity; // "active" | "inactive"
245
- status.ready; // 首次 ready 是否完成
246
- status.freshness; // "fresh" | "stale" | "reconciling"
247
- status.lastReceivedAt; // 最后收到数据的时间
248
- status.reason; // 变 stale 的原因: "ws_disconnected" | "heartbeat_timeout"
249
- }
54
+ await client.stop();
250
55
  ```
251
56
 
252
- #### 退订
57
+ ### 账户与订单
253
58
 
254
59
  ```ts
255
- await client.market.unsubscribeL1Book({
256
- exchange: "binance",
257
- symbol: "BTC/USDT:USDT",
258
- });
259
- ```
260
-
261
- 退订后最后一份快照仍可读,但 `activity` 会变成 `"inactive"`。调用方不应把旧快照当成实时值。
262
-
263
- ### 5. Account(账户余额和仓位)
264
-
265
- > 当前 account 已接通 Binance PAPI UM 私有链路,可读取余额、仓位、风险和账户状态。
60
+ const client = createClient();
61
+ await client.start();
266
62
 
267
- ```ts
268
- // ① 注册账户(start 之前或之后均可)
269
63
  await client.registerAccount({
270
64
  accountId: "main-binance",
271
65
  exchange: "binance",
@@ -275,86 +69,9 @@ await client.registerAccount({
275
69
  },
276
70
  });
277
71
 
278
- await client.start();
279
-
280
- // ② 订阅账户数据流
281
72
  await client.account.subscribeAccount({ accountId: "main-binance" });
282
-
283
- // ③ 读取快照
284
- const snapshot = client.account.getAccountSnapshot("main-binance");
285
- // → AccountSnapshot | undefined
286
-
287
- const balances = client.account.getBalances("main-binance");
288
- // → BalanceSnapshot[]
289
-
290
- const usdtBalance = client.account.getBalance("main-binance", "USDT");
291
- // → BalanceSnapshot | undefined
292
- // { asset: "USDT", free: BigNumber("1000.00"), used: BigNumber("200.00"), total: BigNumber("1200.00"), ... }
293
-
294
- const positions = client.account.getPositions("main-binance");
295
- // → PositionSnapshot[]
296
-
297
- const btcPosition = client.account.getPosition({
298
- accountId: "main-binance",
299
- symbol: "BTC/USDT:USDT",
300
- side: "long", // 可选,不传则返回第一个匹配
301
- });
302
- // → PositionSnapshot | undefined
303
- // { symbol, side, size, entryPrice, markPrice, unrealizedPnl, leverage, ... }
304
-
305
- const risk = client.account.getRiskSnapshot("main-binance");
306
- // → RiskSnapshot | undefined
307
- // { equity, marginRatio, initialMargin, maintenanceMargin, ... }
308
-
309
- // ④ 消费增量事件
310
- for await (const event of client.account.events.updates({
311
- accountId: "main-binance",
312
- })) {
313
- switch (event.type) {
314
- case "balance.updated":
315
- console.log(event.asset, event.snapshot.free);
316
- break;
317
- case "position.updated":
318
- console.log(event.symbol, event.snapshot.size);
319
- break;
320
- case "risk.updated":
321
- console.log(event.snapshot.marginRatio);
322
- break;
323
- case "account.snapshot_replaced":
324
- console.log("全量快照替换");
325
- break;
326
- }
327
- }
328
-
329
- // ⑤ 退订 & 移除账户
330
- await client.account.unsubscribeAccount({ accountId: "main-binance" });
331
- await client.removeAccount("main-binance");
332
- ```
333
-
334
- ### 6. Order(订单)
335
-
336
- > 当前 order 已接通 Binance PAPI UM 订单私有链路,并支持第一版交易命令:`createOrder()`、`cancelOrder()`、`cancelAllOrders()`。
337
-
338
- ```ts
339
- // 订阅订单流(需要先 registerAccount)
340
73
  await client.order.subscribeOrders({ accountId: "main-binance" });
341
74
 
342
- // 读取所有挂单
343
- const openOrders = client.order.getOpenOrders("main-binance");
344
- // → OrderSnapshot[]
345
-
346
- // 按 symbol 过滤挂单
347
- const btcOrders = client.order.getOpenOrders("main-binance", "BTC/USDT:USDT");
348
-
349
- // 按 orderId 或 clientOrderId 查询单笔
350
- const order = client.order.getOrder({
351
- accountId: "main-binance",
352
- orderId: "12345",
353
- });
354
- // → OrderSnapshot | undefined
355
- // { symbol, side, type, status, price, amount, filled, remaining, ... }
356
-
357
- // 下单:第一版只支持 limit / market
358
75
  const created = await client.order.createOrder({
359
76
  accountId: "main-binance",
360
77
  symbol: "BTC/USDT:USDT",
@@ -364,267 +81,36 @@ const created = await client.order.createOrder({
364
81
  amount: "0.001",
365
82
  });
366
83
 
367
- // 如果账户是双向持仓模式(hedge mode),必须显式传 positionSide
368
- const hedgeCreated = await client.order.createOrder({
369
- accountId: "main-binance",
370
- symbol: "BTC/USDT:USDT",
371
- side: "buy",
372
- type: "limit",
373
- price: "71900.9",
374
- amount: "0.001",
375
- positionSide: "long",
376
- });
377
-
378
- // 撤单:需要 accountId + symbol,并提供 orderId / clientOrderId 其一
379
- const canceled = await client.order.cancelOrder({
84
+ await client.order.cancelOrder({
380
85
  accountId: "main-binance",
381
86
  symbol: "BTC/USDT:USDT",
382
87
  orderId: created.orderId,
383
88
  });
384
89
 
385
- // 某个 symbol 下全撤
386
- const canceledAll = await client.order.cancelAllOrders({
387
- accountId: "main-binance",
388
- symbol: "BTC/USDT:USDT",
389
- });
390
-
391
- // 消费订单事件
392
- for await (const event of client.order.events.updates({
393
- accountId: "main-binance",
394
- })) {
395
- switch (event.type) {
396
- case "order.updated":
397
- console.log("订单更新", event.snapshot.status);
398
- break;
399
- case "order.filled":
400
- console.log("完全成交", event.snapshot.avgFillPrice);
401
- break;
402
- case "order.canceled":
403
- console.log("已撤单");
404
- break;
405
- case "order.rejected":
406
- console.log("被拒绝");
407
- break;
408
- }
409
- }
410
-
411
- // 退订
412
- await client.order.unsubscribeOrders({ accountId: "main-binance" });
413
- ```
414
-
415
- `createOrder()` / `cancelOrder()` resolve 的是 REST 成功后标准化的 `OrderSnapshot`;`events.updates()` 是后续生命周期变化流,不是唯一 ack 来源。
416
-
417
- ### 7. 健康监控
418
-
419
- #### 全局健康快照
420
-
421
- ```ts
422
- const health = client.getHealth();
423
- // → ClientHealthSnapshot
424
- // {
425
- // clientStatus: "running",
426
- // markets: MarketDataStatus[], // 所有 market 订阅的状态
427
- // accounts: AccountDataStatus[], // 所有 account 订阅的状态
428
- // orders: OrderDataStatus[], // 所有 order 订阅的状态
429
- // updatedAt: 1710000000000,
430
- // }
431
- ```
432
-
433
- #### 消费健康事件流
434
-
435
- ```ts
436
- for await (const event of client.events.health()) {
437
- switch (event.type) {
438
- case "client.status_changed":
439
- console.log("client 状态变化:", event.status);
440
- break;
441
- case "market.status_changed":
442
- console.log("market 状态变化:", event.exchange, event.symbol);
443
- break;
444
- case "account.status_changed":
445
- console.log("account 状态变化:", event.accountId);
446
- break;
447
- case "order.status_changed":
448
- console.log("order 状态变化:", event.accountId);
449
- break;
450
- }
451
- }
452
- ```
453
-
454
- 可以按 scope 过滤:
455
-
456
- ```ts
457
- // 只关心 market 相关的健康变化
458
- for await (const event of client.events.health({ scope: "market" })) {
459
- // ...
460
- }
461
-
462
- // 只关心特定交易所
463
- for await (const event of client.events.health({ exchange: "binance" })) {
464
- // ...
465
- }
466
- ```
467
-
468
- #### 消费内部错误流
469
-
470
- ```ts
471
- for await (const err of client.events.errors()) {
472
- console.error(`[${err.source}] ${err.error.message}`, {
473
- exchange: err.exchange,
474
- symbol: err.symbol,
475
- accountId: err.accountId,
476
- });
477
- }
478
- ```
479
-
480
- 适合桥接到日志系统或告警系统。
481
-
482
- ### 8. 错误处理
483
-
484
- SDK 的可预期错误统一通过 `AcexError` 抛出,包含结构化的 `code`:
485
-
486
- ```ts
487
- import { AcexError } from "@imbingox/acex";
488
-
489
- try {
490
- await client.market.subscribeL1Book({
491
- exchange: "binance",
492
- symbol: "INVALID/PAIR",
493
- });
494
- } catch (error) {
495
- if (error instanceof AcexError) {
496
- switch (error.code) {
497
- case "CLIENT_NOT_STARTED":
498
- // client 还没 start()
499
- break;
500
- case "MARKET_NOT_FOUND":
501
- // symbol 不存在
502
- break;
503
- case "MARKET_INACTIVE":
504
- // 市场存在但不可交易
505
- break;
506
- case "MARKET_STREAM_TIMEOUT":
507
- // L1 Book 首条消息超时
508
- break;
509
- case "EXCHANGE_NOT_SUPPORTED":
510
- // 交易所未支持
511
- break;
512
- case "MARKET_CATALOG_LOAD_FAILED":
513
- // market catalog 拉取失败
514
- break;
515
- case "ACCOUNT_ALREADY_EXISTS":
516
- // 重复注册同一个 accountId
517
- break;
518
- case "ACCOUNT_NOT_FOUND":
519
- // accountId 不存在
520
- break;
521
- case "CREDENTIALS_MISSING":
522
- // 私有订阅缺少凭证
523
- break;
524
- }
525
- }
526
- }
90
+ await client.stop();
527
91
  ```
528
92
 
529
- ## 完整示例
93
+ 价格、数量等输出字段统一是 `BigNumber`;`createOrder()` 的 `price` / `amount` 输入仍接受 decimal string。详见手册 [§3 核心概念](./docs/api.md#3-核心概念)。
530
94
 
531
- ```ts
532
- import { AcexError, createClient } from "@imbingox/acex";
533
-
534
- async function main() {
535
- const client = createClient({
536
- market: {
537
- l1InitialMessageTimeoutMs: 15_000,
538
- l1StaleAfterMs: 15_000,
539
- },
540
- });
541
-
542
- await client.start();
543
-
544
- // 加载市场列表
545
- await client.market.loadMarkets();
546
- const symbols = client.market.listMarkets().map((m) => m.symbol);
547
- console.log(`共 ${symbols.length} 个交易对`);
548
-
549
- // 订阅 BTC 永续 L1 Book
550
- const exchange = "binance";
551
- const symbol = "BTC/USDT:USDT";
552
-
553
- await client.market.subscribeL1Book({ exchange, symbol });
554
-
555
- // 读取首个快照
556
- const book = client.market.getL1Book({ exchange, symbol });
557
- console.log(`BTC bid=${book?.bidPrice.toFixed()} ask=${book?.askPrice.toFixed()}`);
558
-
559
- // 持续消费 5 条更新后退出
560
- let count = 0;
561
- for await (const event of client.market.events.l1BookUpdates({
562
- exchange,
563
- symbol,
564
- })) {
565
- console.log(
566
- `#${++count} bid=${event.snapshot.bidPrice.toFixed()} ask=${event.snapshot.askPrice.toFixed()}`,
567
- );
568
- if (count >= 5) break;
569
- }
570
-
571
- await client.market.unsubscribeL1Book({ exchange, symbol });
572
- await client.stop();
573
- }
574
-
575
- main().catch((error) => {
576
- if (error instanceof AcexError) {
577
- console.error(error.code, error.message);
578
- } else {
579
- console.error(error);
580
- }
581
- });
582
- ```
583
-
584
- ## 调用顺序总结
95
+ ## 核心能力
585
96
 
586
- ```
587
- createClient()
588
-
589
- registerAccount() 如需私有能力(可选)
590
-
591
- client.start()
592
-
593
- loadMarkets() ← 如需市场列表(可选)
594
-
595
- subscribe*() ← 开始维护数据
596
-
597
- get*() / events.*() ← 读快照 / 消费增量
598
-
599
- unsubscribe*() ← 释放订阅
600
-
601
- removeAccount() ← 释放账户(可选)
602
-
603
- client.stop()
604
- ```
605
-
606
- ## 核心语义
97
+ | 能力 | 概述 | 详细文档 |
98
+ |---|---|---|
99
+ | **Market** | Market catalog、L1 Book / Funding Rate 订阅、增量事件、订阅状态与自动重连 | [docs/api.md §5](./docs/api.md#5-marketmanager) |
100
+ | **Account** | 账户快照、余额、持仓、风险投影与事件流 | [docs/api.md §6](./docs/api.md#6-accountmanager) |
101
+ | **Order** | open orders 投影、订单事件流,`createOrder` / `cancelOrder` / `cancelAllOrders` 第一版命令 | [docs/api.md §7](./docs/api.md#7-ordermanager) |
102
+ | **健康与错误** | `getHealth()`、`events.health()`、`events.errors()` | [docs/api.md §8](./docs/api.md#8-健康与错误事件) |
607
103
 
608
- | 概念 | 说明 |
609
- |---|---|
610
- | `get*()` | 读 SDK 本地快照,不阻塞、不发网络请求 |
611
- | `events.*()` | 返回 `AsyncIterable`,持续消费增量变化 |
612
- | `event.snapshot` vs `get*()` | `event.snapshot` 是事件发生时的快照;`get*()` 是调用时的最新值。跨 symbol 比较建议用 `get*()` |
613
- | `subscribe*()` | ready barrier — `await` 返回时对应的 `get*()` 已可用 |
614
- | `events.*()` 与 `subscribe*()` | 独立。`events` 不会隐式触发订阅,必须显式 `subscribe` |
615
- | 退订后的缓存 | 最后一份快照仍可读,但 `activity` 变为 `"inactive"` |
104
+ 完整手册(接口签名、数据类型、错误码):[docs/api.md](./docs/api.md)。
616
105
 
617
106
  ## 当前限制
618
107
 
619
- - 运行时真正支持的市场数据交易所只有 **Binance**(`okx`、`bybit`、`gate` 仅类型定义)
620
- - 真实落地的 market 数据链路当前是 Binance **L1 Book**
621
- - 私有账户与订单链路当前只支持 **Binance PAPI UM**
622
- - `fundingRate` 接口已暴露,但当前是占位快照
623
- - 第一版交易命令只支持 `createOrder()` / `cancelOrder()` / `cancelAllOrders()`
624
- - `createOrder()` 当前只支持 `limit` / `market`
625
- - 双向持仓模式账户下单时必须显式传 `positionSide`
626
- - 条件单、改单、账户级全撤当前还不支持
627
- - `CreateClientOptions` 中 `sandbox`、`logger`、`logLevel` 仍是预留位
108
+ - 运行时只支持 `binance`;`okx` / `bybit` / `gate` 仅类型定义
109
+ - 私有链路仅 Binance PAPI UM(统一账户 / Portfolio Margin)
110
+ - Funding Rate 仅支持 Binance 永续合约,来自 mark price websocket;不支持现货和交割合约
111
+ - `createOrder()` 只支持 `limit` / `market`;条件单、改单、账户级全撤不支持
112
+ - 双向持仓账户下单时必须显式传 `positionSide`
113
+ - `CreateClientOptions` `sandbox` / `logger` / `logLevel` 是预留位
628
114
 
629
115
  ## 仓库内开发
630
116
 
@@ -632,32 +118,12 @@ client.stop()
632
118
  bun install
633
119
  bun run lint
634
120
  bun run type-check
635
- bun test
121
+ bun run test
636
122
  ```
637
123
 
638
- ### 发布流程
124
+ ### 真实环境 smoke / soak 脚本
639
125
 
640
- 当前仓库使用 **Changesets + GitHub Actions + npm Trusted Publishing**:
641
-
642
- 1. 开发 PR 时,如果改动会影响用户,执行 `bun run changeset`
643
- 2. 按提示选择 `patch` / `minor` / `major`,并写一段对外 release note
644
- 3. PR merge 到 `main` 后,[release.yml](/projects/acex-feat-order_account/.github/workflows/release.yml) 会自动:
645
- - 安装依赖
646
- - 执行 `bun run lint`
647
- - 执行 `bun run type-check`
648
- - 执行 `bun run test`
649
- - 若存在未消费的 changeset,则创建或更新 release PR
650
- 4. merge release PR 后,同一条 workflow 会自动发布到 npm
651
-
652
- 当前仓库处于 Changesets 的 `beta` prerelease 模式,自动发布默认走 npm `beta` dist-tag。
653
-
654
- npm 侧配置 Trusted Publisher 时,需要确保:
655
-
656
- - workflow 文件名是 `release.yml`
657
- - `package.json.repository.url` 必须直接写仓库地址,例如 `https://github.com/imbingox/acex`
658
- - npm 包 settings 里绑定的是 GitHub Actions trusted publisher,而不是长期 `NPM_TOKEN`
659
-
660
- 真实 Binance 公网 smoke test 单独执行,不放进默认 `bun test`:
126
+ 不进默认 `bun run test`,单独执行:
661
127
 
662
128
  ```bash
663
129
  bun run test:live:market:smoke
@@ -668,13 +134,33 @@ bun run test:live:order:smoke
668
134
  bun run test:live:order:soak
669
135
  ```
670
136
 
671
- 这些脚本会验证:
672
- - `market`:`loadMarkets()`、`subscribeL1Book()`、`getL1Book()` / `events.l1BookUpdates()`,以及可选的主动断线后自动重连
137
+ 约定:
138
+
139
+ - `smoke` 默认跑 10 秒,做连通性检查,不主动断线
140
+ - `soak` 默认跑 60 秒,并做一次主动断线重连验证
141
+
142
+ 覆盖内容:
143
+
144
+ - `market`:`loadMarkets()`、`subscribeL1Book()`、`subscribeFundingRate()`、`getL1Book()` / `getFundingRate()`、对应事件流和可选断线重连(`--disconnect-target funding` 可单独验证资金费率重连)
673
145
  - `account`:Binance PAPI UM 账户 bootstrap、余额/仓位/风险投影、private stream 更新和可选重连
674
146
  - `order`:open orders bootstrap、`subscribeOrders()`、订单事件投影和可选重连
675
147
 
676
- 约定:
677
- - `smoke` 是快速连通性检查,默认跑 10 秒,不主动断线
678
- - `soak` 是短时稳定性检查,默认跑 60 秒,并做一次主动断线重连验证
148
+ ### 发布流程
149
+
150
+ 仓库使用 **Changesets + GitHub Actions + npm Trusted Publishing**:
679
151
 
680
- 更完整的公开接口设计说明见 [docs/sdk-public-api.md](./docs/sdk-public-api.md)。
152
+ 1. 开发 PR 时,如果改动影响用户,执行 `bun run changeset`
153
+ 2. 按提示选择 `patch` / `minor` / `major`,写一段对外 release note
154
+ 3. PR merge 到 `main` 后,[release.yml](./.github/workflows/release.yml) 会:
155
+ - 安装依赖
156
+ - 跑 `bun run lint` / `bun run type-check` / `bun run test`
157
+ - 若有未消费的 changeset,创建或更新 release PR
158
+ 4. merge release PR 后,同一 workflow 自动发布到 npm
159
+
160
+ 当前仓库处于 Changesets 的 `beta` prerelease 模式,自动发布默认走 npm `beta` dist-tag。
161
+
162
+ npm 侧配置 Trusted Publisher 要求:
163
+
164
+ - workflow 文件名是 `release.yml`
165
+ - `package.json.repository.url` 直接写仓库地址,例如 `https://github.com/imbingox/acex`
166
+ - npm 包 settings 绑定 GitHub Actions trusted publisher,不使用长期 `NPM_TOKEN`