@dshtrading/connector-okx 0.2.1 → 0.3.0

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/lib/index.d.ts CHANGED
@@ -120,6 +120,15 @@ declare class OkxTradeService extends Service implements TradeService {
120
120
  private auth;
121
121
  /** 规格(带 TTL 缓存;demo/live 分桶)。查不到 → TRADING_UNSUPPORTED_SYMBOL。 */
122
122
  private getInstrument;
123
+ /** 从上游行集收集 instId(只读账户面三个入口共用的预取键;非 SWAP 由 prefetch 内部过滤)。 */
124
+ private instIdsOf;
125
+ /**
126
+ * 规格并发预取(只读账户面冷启动热路径):getPositions/listOpenOrders/listTradeFills 每个
127
+ * 不同 SWAP instId 都要一次 /public/instruments 解析 ctVal;逐行 await 会把 N 个不同标的
128
+ * 放大成 N 次串行往返。这里先按上限并发解析并把成功结果回传,逐行同步取用。
129
+ * 返回 Map 只含成功项:查不到的 instId 缺席,调用点按原语义保留张数原值(不再逐行重试)。
130
+ */
131
+ private prefetchInstruments;
123
132
  /**
124
133
  * 下单(api TradeService 契约)。
125
134
  *
@@ -157,7 +166,10 @@ declare class OkxTradeService extends Service implements TradeService {
157
166
  getPositions(): Promise<Position[]>;
158
167
  /** 只读余额(TradeService 契约外扩展,crypto_get_balance 工具消费)。 */
159
168
  getBalances(): Promise<AccountBalance[]>;
160
- /** SWAP 的 sz/accFillSz/fillSz(张)→ base 币数(规格缓存,非 SWAP 原值)。 */
169
+ /**
170
+ * SWAP 的 sz/accFillSz/fillSz(张)→ base 币数(非 SWAP 原值)。传入已预取的规格 Map
171
+ * 时纯内存查表(缺席即回退原值,不触网);未传时回退单条 getInstrument(带缓存)。
172
+ */
161
173
  private toCoins;
162
174
  /** 当前挂单(TradeService 可选契约,issue #40;只读、需凭证)。 */
163
175
  listOpenOrders(symbol?: string): Promise<Order[]>;
package/lib/index.js CHANGED
@@ -191,6 +191,8 @@ var OkxMarketDataService = class extends Service {
191
191
  }
192
192
  };
193
193
  const INSTRUMENT_TTL_MS = 36e5;
194
+ /** 规格并发预取上限:账户只读面冷缓存时最多同时打这么多 /public/instruments(公共面有限频,取保守并发)。 */
195
+ const INSTRUMENT_PREFETCH_CONCURRENCY = 8;
194
196
  var OkxTradeService = class extends Service {
195
197
  client;
196
198
  config;
@@ -237,6 +239,31 @@ var OkxTradeService = class extends Service {
237
239
  });
238
240
  return row;
239
241
  }
242
+ /** 从上游行集收集 instId(只读账户面三个入口共用的预取键;非 SWAP 由 prefetch 内部过滤)。 */
243
+ instIdsOf(rows) {
244
+ const ids = [];
245
+ for (const row of rows) {
246
+ const instId = row.instId;
247
+ if (typeof instId === "string") ids.push(instId);
248
+ }
249
+ return ids;
250
+ }
251
+ /**
252
+ * 规格并发预取(只读账户面冷启动热路径):getPositions/listOpenOrders/listTradeFills 每个
253
+ * 不同 SWAP instId 都要一次 /public/instruments 解析 ctVal;逐行 await 会把 N 个不同标的
254
+ * 放大成 N 次串行往返。这里先按上限并发解析并把成功结果回传,逐行同步取用。
255
+ * 返回 Map 只含成功项:查不到的 instId 缺席,调用点按原语义保留张数原值(不再逐行重试)。
256
+ */
257
+ async prefetchInstruments(instIds) {
258
+ const distinct = [...new Set([...instIds].filter((id) => id.endsWith("-SWAP")))];
259
+ const resolved = /* @__PURE__ */ new Map();
260
+ for (let i = 0; i < distinct.length; i += INSTRUMENT_PREFETCH_CONCURRENCY) await Promise.all(distinct.slice(i, i + INSTRUMENT_PREFETCH_CONCURRENCY).map(async (id) => {
261
+ try {
262
+ resolved.set(id, await this.getInstrument(id));
263
+ } catch {}
264
+ }));
265
+ return resolved;
266
+ }
240
267
  /**
241
268
  * 下单(api TradeService 契约)。
242
269
  *
@@ -367,6 +394,7 @@ var OkxTradeService = class extends Service {
367
394
  async getPositions() {
368
395
  const credentials = await this.getCredentials();
369
396
  const rows = await this.client.getPositions(this.auth(credentials));
397
+ const instruments = await this.prefetchInstruments(this.instIdsOf(rows));
370
398
  const positions = [];
371
399
  for (const row of rows) {
372
400
  const d = row;
@@ -374,10 +402,10 @@ var OkxTradeService = class extends Service {
374
402
  const pos = typeof d.pos === "string" || typeof d.pos === "number" ? Number(d.pos) : NaN;
375
403
  if (instId === void 0 || !Number.isFinite(pos)) continue;
376
404
  let size = Math.abs(pos);
377
- if (instId.endsWith("-SWAP")) try {
378
- const instrument = await this.getInstrument(instId);
379
- if (instrument.ctVal !== void 0) size = size * instrument.ctVal;
380
- } catch {}
405
+ if (instId.endsWith("-SWAP")) {
406
+ const instrument = instruments.get(instId);
407
+ if (instrument?.ctVal !== void 0) size = size * instrument.ctVal;
408
+ }
381
409
  const posSide = d.posSide === "long" || d.posSide === "short" ? d.posSide : "net";
382
410
  const side = posSide === "net" ? pos >= 0 ? "long" : "short" : posSide;
383
411
  const entryPrice = typeof d.avgPx === "string" ? Number(d.avgPx) : void 0;
@@ -421,11 +449,18 @@ var OkxTradeService = class extends Service {
421
449
  }
422
450
  return balances;
423
451
  }
424
- /** SWAP 的 sz/accFillSz/fillSz(张)→ base 币数(规格缓存,非 SWAP 原值)。 */
425
- async toCoins(instId, exchangeAmount) {
452
+ /**
453
+ * SWAP 的 sz/accFillSz/fillSz(张)→ base 币数(非 SWAP 原值)。传入已预取的规格 Map
454
+ * 时纯内存查表(缺席即回退原值,不触网);未传时回退单条 getInstrument(带缓存)。
455
+ */
456
+ async toCoins(instId, exchangeAmount, instruments) {
426
457
  const n = typeof exchangeAmount === "string" || typeof exchangeAmount === "number" ? Number(exchangeAmount) : NaN;
427
458
  if (!Number.isFinite(n)) return void 0;
428
459
  if (!instId.endsWith("-SWAP")) return n;
460
+ if (instruments !== void 0) {
461
+ const instrument = instruments.get(instId);
462
+ return instrument?.ctVal !== void 0 ? n * instrument.ctVal : n;
463
+ }
429
464
  try {
430
465
  const instrument = await this.getInstrument(instId);
431
466
  return instrument.ctVal !== void 0 ? n * instrument.ctVal : n;
@@ -438,15 +473,16 @@ var OkxTradeService = class extends Service {
438
473
  const instId = symbol !== void 0 && symbol !== "" ? normalizeOkxSymbol(symbol) : void 0;
439
474
  const credentials = await this.getCredentials();
440
475
  const rows = await this.client.listPendingOrders(instId, this.auth(credentials));
476
+ const instruments = await this.prefetchInstruments(this.instIdsOf(rows));
441
477
  const orders = [];
442
478
  for (const row of rows) {
443
479
  const d = row;
444
480
  const ordId = typeof d.ordId === "string" ? d.ordId : void 0;
445
481
  const rawInstId = typeof d.instId === "string" ? d.instId : void 0;
446
482
  if (ordId === void 0 || rawInstId === void 0) continue;
447
- const quantity = await this.toCoins(rawInstId, d.sz);
483
+ const quantity = await this.toCoins(rawInstId, d.sz, instruments);
448
484
  if (quantity === void 0) continue;
449
- const filledQuantity = await this.toCoins(rawInstId, d.accFillSz);
485
+ const filledQuantity = await this.toCoins(rawInstId, d.accFillSz, instruments);
450
486
  const price = typeof d.px === "string" && d.px !== "" ? Number(d.px) : typeof d.avgPx === "string" && d.avgPx !== "" ? Number(d.avgPx) : void 0;
451
487
  const state = typeof d.state === "string" ? d.state : "";
452
488
  const timestamp = typeof d.uTime === "string" ? Number(d.uTime) : typeof d.cTime === "string" ? Number(d.cTime) : Date.now();
@@ -471,13 +507,14 @@ var OkxTradeService = class extends Service {
471
507
  const capped = Math.max(1, Math.min(Math.floor(limit) || 50, 100));
472
508
  const credentials = await this.getCredentials();
473
509
  const rows = await this.client.listFillsHistory(instId, capped, this.auth(credentials));
510
+ const instruments = await this.prefetchInstruments(this.instIdsOf(rows));
474
511
  const fills = [];
475
512
  for (const row of rows) {
476
513
  const d = row;
477
514
  const rawInstId = typeof d.instId === "string" ? d.instId : void 0;
478
515
  const price = pickNumber(d.fillPx);
479
516
  if (rawInstId === void 0 || price === void 0) continue;
480
- const amount = await this.toCoins(rawInstId, d.fillSz);
517
+ const amount = await this.toCoins(rawInstId, d.fillSz, instruments);
481
518
  if (amount === void 0) continue;
482
519
  const fee = pickNumber(d.fee);
483
520
  const feeAsset = typeof d.feeCcy === "string" && d.feeCcy !== "" ? d.feeCcy : void 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dshtrading/connector-okx",
3
3
  "description": "OKX market connector plugin for dsh-trading: public market data + signed demo/live trading (R1-R3), mutual-exclusion activation with connector-binance",
4
- "version": "0.2.1",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -21,8 +21,8 @@
21
21
  ],
22
22
  "license": "PolyForm-Noncommercial-1.0.0",
23
23
  "dependencies": {
24
- "@dshtrading/api": "^0.2.1",
25
- "@dshtrading/indicators": "^0.2.1"
24
+ "@dshtrading/api": "^0.3.0",
25
+ "@dshtrading/indicators": "^0.3.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@deepseek-ai/cordis": ">=4.0.0",
@@ -32,7 +32,7 @@
32
32
  "devDependencies": {
33
33
  "tsdown": "^0.22.0",
34
34
  "vitest": "^3.0.0",
35
- "@dshtrading/connector-binance": "^0.2.1"
35
+ "@dshtrading/connector-binance": "^0.3.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsdown",