_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 }