@dshtrading/connector-tencent 0.1.3 → 0.1.5

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
@@ -40,6 +40,11 @@ declare class TencentMarketDataService extends Service implements MarketDataServ
40
40
  */
41
41
  getOrderbook(symbol: string): Promise<Orderbook>;
42
42
  getKlines(symbol: string, interval: Interval, limit?: number): Promise<Kline[]>;
43
+ listInstruments(query?: string): Promise<Array<{
44
+ symbol: string;
45
+ name: string;
46
+ pinyin?: string;
47
+ }>>;
43
48
  subscribeTicker(symbol: string, cb: (ticker: Ticker) => void, options?: SubscribeTickerOptions): Disposable;
44
49
  }
45
50
  /** <market>_place_order 参数契约(dryRun 缺省 true)。 */
package/lib/index.js CHANGED
@@ -46,6 +46,9 @@ var TencentMarketDataService = class extends Service {
46
46
  getKlines(symbol, interval, limit) {
47
47
  return this.client.getKlines(symbol, interval, limit);
48
48
  }
49
+ listInstruments(query) {
50
+ return this.client.listInstruments(query);
51
+ }
49
52
  subscribeTicker(symbol, cb, options) {
50
53
  const ms = Math.max(options?.intervalMs ?? SUBSCRIBE_DEFAULT_MS, SUBSCRIBE_MIN_MS);
51
54
  const tick = () => {
package/lib/rest.d.ts CHANGED
@@ -8,8 +8,8 @@ declare class TradingServiceError extends Error {
8
8
  type TencentMarket = 'cn' | 'hk';
9
9
  /**
10
10
  * 规范化 A 股符号:接受 `600519` / `SH600519` / `sh600519` / `sz000001`,统一为
11
- * 腾讯 wire 形态小写 `<sh|sz><6位数字>`。6/9 开头→sh(沪,含科创板 688),0/3 开头→sz
12
- * (深,含创业板 300)。4/8 开头(北交所)不在本切片支持范围。
11
+ * 腾讯 wire 形态小写 `<sh|sz><6位数字>`。6/9 开头→sh(沪,含科创板 688),5 开头→sh(沪市基金/ETF),
12
+ * 0/3 开头→sz(深,含创业板 300;知名上海指数 000688/000300 等推断为 sh)。4/8 开头(北交所)不在本切片支持范围。
13
13
  */
14
14
  declare function normalizeCnSymbol(symbol: string): string;
15
15
  /**
@@ -94,6 +94,8 @@ interface TencentRestOptions {
94
94
  readonly klineBaseUrl?: string;
95
95
  /** 覆盖分钟 K 线 base(测试/反代用),末尾不带斜杠。 */
96
96
  readonly mklineBaseUrl?: string;
97
+ /** 覆盖智能联想搜索 base(测试/反代用),末尾不带斜杠。 */
98
+ readonly searchBaseUrl?: string;
97
99
  /** 单请求超时(ms),默认 10s。 */
98
100
  readonly timeoutMs?: number;
99
101
  /** 注入 fetch 实现;缺省用全局 fetch(Node 22+ 内置)。 */
@@ -124,6 +126,15 @@ declare class TencentRestClient {
124
126
  * 港股 mkline 端点不支持分钟线,请求 5m/30m 会抛出 TRADING_UNSUPPORTED_INTERVAL。
125
127
  */
126
128
  getKlines(symbol: string, interval: Interval, limit?: number): Promise<Kline[]>;
129
+ /**
130
+ * 智能联想标的列表(按名称、代码、拼音模糊检索):
131
+ * 接入腾讯证券智能联想端点,统一格式输出标的 symbol、name、pinyin。
132
+ */
133
+ listInstruments(query?: string): Promise<Array<{
134
+ symbol: string;
135
+ name: string;
136
+ pinyin?: string;
137
+ }>>;
127
138
  }
128
139
  //#endregion
129
140
  export { CnTickerExtra, HkTickerExtra, INTERVAL_VOCABULARY, TencentFundamentals, TencentIntervalDef, TencentKlineType, TencentMarket, TencentRestClient, TencentRestOptions, TencentTicker, TradingServiceError, klineDateToEpochMs, minuteKlineTimeToEpochMs, normalizeCnSymbol, normalizeHkSymbol, normalizeSymbol, parseCnOrderbook, toCanonicalTencentSymbol, wallTimeToEpochMs };
package/lib/rest.js CHANGED
@@ -10,11 +10,18 @@ var TradingServiceError = class extends Error {
10
10
  }
11
11
  };
12
12
  const CN_SYMBOL_PATTERN = /^(?:(sh|sz)(\d{6})|(\d{6})(?:\.(sh|sz))?)$/;
13
+ const KNOWN_SH_INDICES = /* @__PURE__ */ new Set([
14
+ "000688",
15
+ "000300",
16
+ "000016",
17
+ "000905",
18
+ "000852"
19
+ ]);
13
20
  const HK_SYMBOL_PATTERN = /^(?:r_hk|hk)?([a-z0-9^]{1,10})(?:\.hk)?$/i;
14
21
  /**
15
22
  * 规范化 A 股符号:接受 `600519` / `SH600519` / `sh600519` / `sz000001`,统一为
16
- * 腾讯 wire 形态小写 `<sh|sz><6位数字>`。6/9 开头→sh(沪,含科创板 688),0/3 开头→sz
17
- * (深,含创业板 300)。4/8 开头(北交所)不在本切片支持范围。
23
+ * 腾讯 wire 形态小写 `<sh|sz><6位数字>`。6/9 开头→sh(沪,含科创板 688),5 开头→sh(沪市基金/ETF),
24
+ * 0/3 开头→sz(深,含创业板 300;知名上海指数 000688/000300 等推断为 sh)。4/8 开头(北交所)不在本切片支持范围。
18
25
  */
19
26
  function normalizeCnSymbol(symbol) {
20
27
  if (typeof symbol !== "string" || !symbol.trim()) throw new TradingServiceError("TRADING_UNSUPPORTED_SYMBOL", "Symbol must be a non-empty string, e.g. 600519 / SH600519 / sz000001");
@@ -22,9 +29,10 @@ function normalizeCnSymbol(symbol) {
22
29
  const m = CN_SYMBOL_PATTERN.exec(raw);
23
30
  if (!m) throw new TradingServiceError("TRADING_UNSUPPORTED_SYMBOL", `Symbol ${JSON.stringify(symbol)} is not a valid CN A-share symbol (expected 6-digit code, optionally SH/SZ prefixed)`);
24
31
  if (m[1]) return `${m[1]}${m[2]}`;
25
- const code = m[3];
32
+ const code = m[3] ?? "";
33
+ if (!code) throw new TradingServiceError("TRADING_UNSUPPORTED_SYMBOL", `Symbol ${JSON.stringify(symbol)} is not a valid CN A-share symbol (expected 6-digit code, optionally SH/SZ prefixed)`);
26
34
  if (m[4]) return `${m[4]}${code}`;
27
- return `${code.startsWith("6") || code.startsWith("9") ? "sh" : "sz"}${code}`;
35
+ return `${code.startsWith("6") || code.startsWith("9") || code.startsWith("5") || KNOWN_SH_INDICES.has(code) ? "sh" : "sz"}${code}`;
28
36
  }
29
37
  /**
30
38
  * 规范化港股符号:接受 `00700` / `700`(1-5 位数字,不足 5 位左补零,统一为 5 位数字
@@ -300,12 +308,14 @@ function parseHkFundamentals(fields, timestamp) {
300
308
  const DEFAULT_QUOTE_BASE_URL = "https://qt.gtimg.cn";
301
309
  const DEFAULT_KLINE_BASE_URL = "https://web.ifzq.gtimg.cn";
302
310
  const DEFAULT_MKLINE_BASE_URL = "https://ifzq.gtimg.cn";
311
+ const DEFAULT_SEARCH_BASE_URL = "https://smartbox.gtimg.cn";
303
312
  const DEFAULT_TIMEOUT_MS = 1e4;
304
313
  var TencentRestClient = class {
305
314
  #market;
306
315
  #quoteBaseUrl;
307
316
  #klineBaseUrl;
308
317
  #mklineBaseUrl;
318
+ #searchBaseUrl;
309
319
  #timeoutMs;
310
320
  #fetchImpl;
311
321
  constructor(market, options = {}) {
@@ -313,6 +323,7 @@ var TencentRestClient = class {
313
323
  this.#quoteBaseUrl = options.quoteBaseUrl ?? DEFAULT_QUOTE_BASE_URL;
314
324
  this.#klineBaseUrl = options.klineBaseUrl ?? DEFAULT_KLINE_BASE_URL;
315
325
  this.#mklineBaseUrl = options.mklineBaseUrl ?? DEFAULT_MKLINE_BASE_URL;
326
+ this.#searchBaseUrl = options.searchBaseUrl ?? DEFAULT_SEARCH_BASE_URL;
316
327
  this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
317
328
  this.#fetchImpl = options.fetchImpl ?? ((input, init) => globalThis.fetch(input, init));
318
329
  }
@@ -444,6 +455,53 @@ var TencentRestClient = class {
444
455
  }
445
456
  return klines;
446
457
  }
458
+ /**
459
+ * 智能联想标的列表(按名称、代码、拼音模糊检索):
460
+ * 接入腾讯证券智能联想端点,统一格式输出标的 symbol、name、pinyin。
461
+ */
462
+ async listInstruments(query) {
463
+ const q = query?.trim();
464
+ if (!q) return [];
465
+ const url = `${this.#searchBaseUrl}/s3/?t=all&q=${encodeURIComponent(q)}`;
466
+ let text = "";
467
+ try {
468
+ const res = await this.#fetchImpl(url);
469
+ if (!res.ok) return [];
470
+ text = await res.text();
471
+ } catch {
472
+ return [];
473
+ }
474
+ const m = /v_hint="([^"]*)"/.exec(text);
475
+ if (!m || !m[1]) return [];
476
+ const records = m[1].replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))).split("^").filter(Boolean);
477
+ const results = [];
478
+ const seen = /* @__PURE__ */ new Set();
479
+ for (const rec of records) {
480
+ const parts = rec.split("~");
481
+ if (parts.length < 3) continue;
482
+ const [mkt, code, name, pinyin] = parts;
483
+ if (!mkt || !code || !name) continue;
484
+ let canonicalSymbol = "";
485
+ if (this.#market === "cn") {
486
+ const lowerMkt = mkt.toLowerCase();
487
+ if (lowerMkt === "sh") canonicalSymbol = `${code}.SH`;
488
+ else if (lowerMkt === "sz") canonicalSymbol = `${code}.SZ`;
489
+ else if (lowerMkt === "bj") canonicalSymbol = `${code}.BJ`;
490
+ else continue;
491
+ } else if (this.#market === "hk") {
492
+ if (mkt.toLowerCase() === "hk") canonicalSymbol = /^\d+$/.test(code) ? `${code.padStart(5, "0")}.HK` : `${code.toUpperCase()}.HK`;
493
+ else continue;
494
+ } else continue;
495
+ if (seen.has(canonicalSymbol)) continue;
496
+ seen.add(canonicalSymbol);
497
+ results.push({
498
+ symbol: canonicalSymbol,
499
+ name,
500
+ ...pinyin ? { pinyin: pinyin.toUpperCase() } : {}
501
+ });
502
+ }
503
+ return results;
504
+ }
447
505
  };
448
506
  //#endregion
449
507
  export { INTERVAL_VOCABULARY, TencentRestClient, TradingServiceError, klineDateToEpochMs, minuteKlineTimeToEpochMs, normalizeCnSymbol, normalizeHkSymbol, normalizeSymbol, parseCnOrderbook, toCanonicalTencentSymbol, wallTimeToEpochMs };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dshtrading/connector-tencent",
3
3
  "description": "CN + HK dual-market connector plugin for dsh-trading: public market data from Tencent quote/kline endpoints implementing the @dshtrading/api contracts (single package, per-instance market split; no key, no official authorization — personal use boundary at your own discretion)",
4
- "version": "0.1.3",
4
+ "version": "0.1.5",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "license": "PolyForm-Noncommercial-1.0.0",
23
23
  "dependencies": {
24
- "@dshtrading/api": "^0.1.3"
24
+ "@dshtrading/api": "^0.1.5"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@deepseek-ai/cordis": ">=4.0.0",