/ clis / eastmoney / _secid.js
_secid.js
 1  // Shared helpers for resolving eastmoney "secid" (市场.代码).
 2  //
 3  // Markets:
 4  //   1.XXXXXX → Shanghai A (SSE)
 5  //   0.XXXXXX → Shenzhen A (SZSE) or Beijing (BSE) — eastmoney groups both under 0
 6  //   116.XXXXX → Hong Kong
 7  //   105.SYMBOL → NASDAQ
 8  //   106.SYMBOL → NYSE
 9  //   107.SYMBOL → AMEX (US)
10  
11  const A_PREFIX_TO_MARKET = /** @param {string} c */ (c) => {
12    if (/^(60|68|90|113|900)/.test(c)) return '1';          // SH (A + STAR + old B)
13    if (/^(00|30|20)/.test(c)) return '0';                  // SZ (A + ChiNext + B)
14    if (/^(4|8|920|83|87)/.test(c)) return '0';             // BJ (eastmoney uses 0.)
15    return '0';
16  };
17  
18  /**
19   * Resolve various user inputs to an eastmoney `secid`.
20   *  - "600000"         → "1.600000"
21   *  - "sh600000"       → "1.600000"
22   *  - "sz000001"       → "0.000001"
23   *  - "bj430047"       → "0.430047"
24   *  - "hk00700" / "00700.HK" → "116.00700"
25   *  - "us.AAPL" / "AAPL" → "105.AAPL"
26   *  - "1.600000"       → passed through
27   * @param {string} input
28   * @returns {string}
29   */
30  // Known eastmoney market numeric prefixes. Narrow whitelist so that inputs like
31  // "00700.HK" are NOT mistakenly treated as secids just because they look like
32  // "<digits>.<alphanumeric>".
33  const KNOWN_MARKET_PREFIXES = new Set(['0', '1', '100', '105', '106', '107', '116', '140', '150', '151', '152', '155', '156']);
34  
35  export function resolveSecid(input) {
36    const raw = String(input || '').trim();
37    if (!raw) throw new Error('empty symbol');
38    const secidMatch = raw.match(/^(\d{1,3})\.([A-Za-z0-9]+)$/);
39    if (secidMatch && KNOWN_MARKET_PREFIXES.has(secidMatch[1])) return raw; // already a secid
40    const lower = raw.toLowerCase();
41  
42    // market-prefixed Chinese code
43    const pref = lower.match(/^(sh|sz|bj)(\d{6})$/);
44    if (pref) {
45      const [, mk, code] = pref;
46      return (mk === 'sh' ? '1' : '0') + '.' + code;
47    }
48  
49    // hk prefix
50    const hk = lower.match(/^hk(\d{4,5})$/) || lower.match(/^(\d{4,5})\.hk$/);
51    if (hk) return '116.' + hk[1].padStart(5, '0');
52  
53    // us.SYMBOL or SYMBOL.N/.O  (treat all as NASDAQ by default; .N as NYSE)
54    const usDot = lower.match(/^([a-z.\-]+)\.([no])$/);
55    if (usDot) return (usDot[2] === 'n' ? '106' : '105') + '.' + usDot[1].toUpperCase();
56    const usPref = lower.match(/^us\.([a-z.\-]+)$/);
57    if (usPref) return '105.' + usPref[1].toUpperCase();
58  
59    // bare 6-digit Chinese code
60    if (/^\d{6}$/.test(raw)) return A_PREFIX_TO_MARKET(raw) + '.' + raw;
61  
62    // bare US ticker — uppercase letters only
63    if (/^[A-Z.\-]{1,8}$/.test(raw)) return '105.' + raw;
64  
65    throw new Error(`Unrecognized symbol: ${input}`);
66  }
67  
68  /**
69   * Normalize a list of user inputs separated by comma / space / Chinese comma.
70   * @param {string} s
71   * @returns {string[]}
72   */
73  export function splitSymbols(s) {
74    return String(s || '')
75      .split(/[,,\s]+/)
76      .map((x) => x.trim())
77      .filter(Boolean);
78  }