匹配范围
*://*.eastmoney.com/*eastmoney-stock-quote东方财富股票实时行情 用途 查询一个或多个股票的实时行情,支持 A 股、港股和美股常见代码格式,返回价格、涨跌幅、成交量、成交额和估值字段。 执行前提 先打开东方财富相关页面,例如 https://quote.eastmoney.com/,再在当前标签页执行。接口为公开行情接口,通常无需登录;若访问频率过高、网络受限或接口策略调整,可能返回空数据或请求失败…
*://*.eastmoney.com/*dom.read1const rawParams = params && typeof params === "object" ? params : {};2 3const FIELDS = [4 "f12",5 "f13",6 "f14",7 "f2",8 "f3",9 "f4",10 "f5",11 "f6",12 "f7",13 "f8",14 "f9",15 "f10",16 "f15",17 "f16",18 "f17",19 "f18",20 "f20",21 "f21",22 "f23",23].join(",");24 25const KNOWN_MARKET_PREFIXES = new Set([26 "0",27 "1",28 "100",29 "105",30 "106",31 "107",32 "116",33 "140",34 "150",35 "151",36 "152",37 "155",38 "156",39]);40 41function splitSymbols(value) {42 if (Array.isArray(value)) {43 return value.map(item => String(item || "").trim()).filter(Boolean);44 }45 return String(value || "")46 .split(/[,,\s]+/)47 .map(item => item.trim())48 .filter(Boolean);49}50 51function chineseMarketPrefix(code) {52 if (/^(60|68|90|113|900)/.test(code)) return "1";53 if (/^(00|30|20)/.test(code)) return "0";54 if (/^(4|8|920|83|87)/.test(code)) return "0";55 return "0";56}57 58function resolveSecid(input) {59 const raw = String(input || "").trim();60 if (!raw) throw new Error("symbol cannot be empty");61 62 const secidMatch = raw.match(/^(\d{1,3})\.([A-Za-z0-9]+)$/);63 if (secidMatch && KNOWN_MARKET_PREFIXES.has(secidMatch[1])) return raw;64 65 const lower = raw.toLowerCase();66 const prefixedChina = lower.match(/^(sh|sz|bj)(\d{6})$/);67 if (prefixedChina) {68 return `${prefixedChina[1] === "sh" ? "1" : "0"}.${prefixedChina[2]}`;69 }70 71 const hk = lower.match(/^hk(\d{4,5})$/) || lower.match(/^(\d{4,5})\.hk$/);72 if (hk) return `116.${hk[1].padStart(5, "0")}`;73 74 const usDot = lower.match(/^([a-z.\-]+)\.([no])$/);75 if (usDot) return `${usDot[2] === "n" ? "106" : "105"}.${usDot[1].toUpperCase()}`;76 77 const usPref = lower.match(/^us\.([a-z.\-]+)$/);78 if (usPref) return `105.${usPref[1].toUpperCase()}`;79 80 if (/^\d{6}$/.test(raw)) return `${chineseMarketPrefix(raw)}.${raw}`;81 if (/^[A-Za-z.\-]{1,8}$/.test(raw)) return `105.${raw.toUpperCase()}`;82 83 throw new Error(`Unrecognized symbol: ${raw}`);84}85 86function marketLabel(value) {87 if (value === 1) return "SH";88 if (value === 0) return "SZ/BJ";89 if (value === 116) return "HK";90 if (value === 105 || value === 106 || value === 107) return "US";91 return String(value ?? "");92}93 94async function readJson(response) {95 try {96 return await response.json();97 } catch {98 throw new Error("Response is not valid JSON");99 }100}101 102const symbols = splitSymbols(rawParams.symbols ?? rawParams.symbol ?? "SH600519");103if (symbols.length === 0) throw new Error("symbols cannot be empty");104 105const secids = symbols.map(resolveSecid);106const url = new URL("https://push2.eastmoney.com/api/qt/ulist.np/get");107url.searchParams.set("secids", secids.join(","));108url.searchParams.set("fltt", "2");109url.searchParams.set("fields", FIELDS);110url.searchParams.set("ut", "bd1d9ddb04089700cf9c27f6f7426281");111 112const response = await fetch(url.toString(), {113 headers: { Accept: "application/json" },114 credentials: "omit",115});116 117if (!response.ok) {118 throw new Error(`Quote request failed with HTTP ${response.status}`);119}120 121const payload = await readJson(response);122const rows = Array.isArray(payload?.data?.diff) ? payload.data.diff : [];123 124if (rows.length === 0) {125 throw new Error(`No quote data returned for ${symbols.join(", ")}`);126}127 128return rows.map(item => ({129 code: item.f12,130 name: item.f14,131 market: marketLabel(item.f13),132 price: item.f2,133 changePercent: item.f3,134 change: item.f4,135 open: item.f17,136 high: item.f15,137 low: item.f16,138 prevClose: item.f18,139 volume: item.f5,140 turnover: item.f6,141 turnoverRate: item.f8,142 amplitude: item.f7,143 peDynamic: item.f9,144 volumeRatio: item.f10,145 priceBook: item.f23,146 marketCap: item.f20,147 floatMarketCap: item.f21,148}));