fix: 海外基金前10重仓股票数据
This commit is contained in:
100
app/api/fund.js
100
app/api/fund.js
@@ -341,8 +341,12 @@ export const fetchFundData = async (c) => {
|
|||||||
let name = '';
|
let name = '';
|
||||||
let weight = '';
|
let weight = '';
|
||||||
if (idxCode >= 0 && tds[idxCode]) {
|
if (idxCode >= 0 && tds[idxCode]) {
|
||||||
const m = tds[idxCode].match(/(\d{6})/);
|
const raw = String(tds[idxCode] || '').trim();
|
||||||
code = m ? m[1] : tds[idxCode];
|
const mA = raw.match(/(\d{6})/);
|
||||||
|
const mHK = raw.match(/(\d{5})/);
|
||||||
|
// 海外股票常见为英文代码(如 AAPL / usAAPL / TSLA.US / 0700.HK)
|
||||||
|
const mAlpha = raw.match(/\b([A-Za-z]{1,10})\b/);
|
||||||
|
code = mA ? mA[1] : (mHK ? mHK[1] : (mAlpha ? mAlpha[1].toUpperCase() : raw));
|
||||||
} else {
|
} else {
|
||||||
const codeIdx = tds.findIndex(txt => /^\d{6}$/.test(txt));
|
const codeIdx = tds.findIndex(txt => /^\d{6}$/.test(txt));
|
||||||
if (codeIdx >= 0) code = tds[codeIdx];
|
if (codeIdx >= 0) code = tds[codeIdx];
|
||||||
@@ -365,20 +369,67 @@ export const fetchFundData = async (c) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
holdings = holdings.slice(0, 10);
|
holdings = holdings.slice(0, 10);
|
||||||
const needQuotes = holdings.filter(h => /^\d{6}$/.test(h.code) || /^\d{5}$/.test(h.code));
|
const normalizeTencentCode = (input) => {
|
||||||
|
const raw = String(input || '').trim();
|
||||||
|
if (!raw) return null;
|
||||||
|
// already normalized tencent styles (normalize prefix casing)
|
||||||
|
const mPref = raw.match(/^(us|hk|sh|sz|bj)(.+)$/i);
|
||||||
|
if (mPref) {
|
||||||
|
const p = mPref[1].toLowerCase();
|
||||||
|
const rest = String(mPref[2] || '').trim();
|
||||||
|
// usAAPL / usIXIC: rest use upper; hk00700 keep digits
|
||||||
|
return `${p}${/^\d+$/.test(rest) ? rest : rest.toUpperCase()}`;
|
||||||
|
}
|
||||||
|
const mSPref = raw.match(/^s_(sh|sz|bj|hk)(.+)$/i);
|
||||||
|
if (mSPref) {
|
||||||
|
const p = mSPref[1].toLowerCase();
|
||||||
|
const rest = String(mSPref[2] || '').trim();
|
||||||
|
return `s_${p}${/^\d+$/.test(rest) ? rest : rest.toUpperCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A股/北证
|
||||||
|
if (/^\d{6}$/.test(raw)) {
|
||||||
|
const pfx =
|
||||||
|
raw.startsWith('6') || raw.startsWith('9')
|
||||||
|
? 'sh'
|
||||||
|
: raw.startsWith('4') || raw.startsWith('8')
|
||||||
|
? 'bj'
|
||||||
|
: 'sz';
|
||||||
|
return `s_${pfx}${raw}`;
|
||||||
|
}
|
||||||
|
// 港股(数字)
|
||||||
|
if (/^\d{5}$/.test(raw)) return `s_hk${raw}`;
|
||||||
|
|
||||||
|
// 形如 0700.HK / 00001.HK
|
||||||
|
const mHkDot = raw.match(/^(\d{4,5})\.(?:HK)$/i);
|
||||||
|
if (mHkDot) return `s_hk${mHkDot[1].padStart(5, '0')}`;
|
||||||
|
|
||||||
|
// 形如 AAPL / TSLA.US / AAPL.O / BRK.B(腾讯接口对“.”支持不稳定,优先取主代码)
|
||||||
|
const mUsDot = raw.match(/^([A-Za-z]{1,10})(?:\.[A-Za-z]{1,6})$/);
|
||||||
|
if (mUsDot) return `us${mUsDot[1].toUpperCase()}`;
|
||||||
|
if (/^[A-Za-z]{1,10}$/.test(raw)) return `us${raw.toUpperCase()}`;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTencentVarName = (tencentCode) => {
|
||||||
|
const cd = String(tencentCode || '').trim();
|
||||||
|
if (!cd) return '';
|
||||||
|
// s_* uses v_s_*
|
||||||
|
if (/^s_/i.test(cd)) return `v_${cd}`;
|
||||||
|
// us/hk/sh/sz/bj uses v_{code}
|
||||||
|
return `v_${cd}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const needQuotes = holdings
|
||||||
|
.map((h) => ({
|
||||||
|
h,
|
||||||
|
tencentCode: normalizeTencentCode(h.code),
|
||||||
|
}))
|
||||||
|
.filter((x) => Boolean(x.tencentCode));
|
||||||
if (needQuotes.length) {
|
if (needQuotes.length) {
|
||||||
try {
|
try {
|
||||||
const tencentCodes = needQuotes.map(h => {
|
const tencentCodes = needQuotes.map((x) => x.tencentCode).join(',');
|
||||||
const cd = String(h.code || '');
|
|
||||||
if (/^\d{6}$/.test(cd)) {
|
|
||||||
const pfx = cd.startsWith('6') || cd.startsWith('9') ? 'sh' : ((cd.startsWith('4') || cd.startsWith('8')) ? 'bj' : 'sz');
|
|
||||||
return `s_${pfx}${cd}`;
|
|
||||||
}
|
|
||||||
if (/^\d{5}$/.test(cd)) {
|
|
||||||
return `s_hk${cd}`;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}).filter(Boolean).join(',');
|
|
||||||
if (!tencentCodes) {
|
if (!tencentCodes) {
|
||||||
resolveH(holdings);
|
resolveH(holdings);
|
||||||
return;
|
return;
|
||||||
@@ -388,22 +439,15 @@ export const fetchFundData = async (c) => {
|
|||||||
const scriptQuote = document.createElement('script');
|
const scriptQuote = document.createElement('script');
|
||||||
scriptQuote.src = quoteUrl;
|
scriptQuote.src = quoteUrl;
|
||||||
scriptQuote.onload = () => {
|
scriptQuote.onload = () => {
|
||||||
needQuotes.forEach(h => {
|
needQuotes.forEach(({ h, tencentCode }) => {
|
||||||
const cd = String(h.code || '');
|
const varName = getTencentVarName(tencentCode);
|
||||||
let varName = '';
|
const dataStr = varName ? window[varName] : null;
|
||||||
if (/^\d{6}$/.test(cd)) {
|
|
||||||
const pfx = cd.startsWith('6') || cd.startsWith('9') ? 'sh' : ((cd.startsWith('4') || cd.startsWith('8')) ? 'bj' : 'sz');
|
|
||||||
varName = `v_s_${pfx}${cd}`;
|
|
||||||
} else if (/^\d{5}$/.test(cd)) {
|
|
||||||
varName = `v_s_hk${cd}`;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const dataStr = window[varName];
|
|
||||||
if (dataStr) {
|
if (dataStr) {
|
||||||
const parts = dataStr.split('~');
|
const parts = dataStr.split('~');
|
||||||
if (parts.length > 5) {
|
const isUS = /^us/i.test(String(tencentCode || ''));
|
||||||
h.change = parseFloat(parts[5]);
|
const idx = isUS ? 32 : 5;
|
||||||
|
if (parts.length > idx) {
|
||||||
|
h.change = parseFloat(parts[idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user