// FRED 發布日程:補足 BLS/BEA 未涵蓋的重要美國總經(零售、房市、ADP、初領失業金等) const UA = 'finance-dashboard/1.0'; const FRED_RELEASES = [ { id: 9, title: '零售銷售', impact: 'medium', time: '08:30 美東', note: '全美零售與餐飲銷售,反映內需動能' }, { id: 13, title: '工業生產 / 產能利用率', impact: 'medium', time: '09:15 美東', note: '工廠產出與產能使用率,景氣先行指標' }, { id: 91, title: '密西根消費者信心', impact: 'medium', time: '10:00 美東', note: '消費者對景氣與通膨的預期,影響風險偏好' }, { id: 27, title: '新屋開工 / 營建許可', impact: 'medium', time: '08:30 美東', note: '住宅建築活動,利率敏感指標' }, { id: 291, title: '成屋銷售', impact: 'medium', time: '10:00 美東', note: '既有住宅成交,觀察房市需求' }, { id: 95, title: '耐久財 / 工廠接單出貨', impact: 'medium', time: '08:30 美東', note: '企業資本支出與製造需求' }, { id: 14, title: '消費信貸', impact: 'low', time: '15:00 美東', note: '家庭與信用卡借款,觀察消費支撐' }, { id: 180, title: '初領失業救濟金', impact: 'medium', time: '08:30 美東', note: '每週勞動市場溫度,突增常引發避險' }, { id: 194, title: 'ADP 私部門就業', impact: 'medium', time: '08:15 美東', note: '非農公布前的私部門就業參考' }, { id: 351, title: '費城 Fed 製造業指數', impact: 'medium', time: '08:30 美東', note: '製造業景氣調查,PMI 類指標' }, { id: 352, title: '非製造業景氣調查', impact: 'medium', time: '10:00 美東', note: '服務業活動,占美國經濟比重大' }, { id: 219, title: '芝加哥 Fed 全國活動指數', impact: 'low', time: '08:30 美東', note: '綜合 85 項經濟指標的月度摘要' }, { id: 11, title: '就業成本指數 ECI', impact: 'medium', time: '08:30 美東', note: '薪資與福利成本,Fed 關注的通膨壓力' }, { id: 16, title: '生產力與單位成本', impact: 'medium', time: '08:30 美東', note: '企業效率與人工成本,影響獲利與通膨' }, ]; function getFredKey() { const key = process.env.FRED_API_KEY; if (!key || key === 'your_fred_api_key_here') return null; return key; } const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function fetchReleaseDates(releaseId, key) { const url = `https://api.stlouisfed.org/fred/release/dates?release_id=${releaseId}` + `&include_release_dates_with_no_data=true&limit=100&sort_order=desc&api_key=${key}&file_type=json`; for (let attempt = 0; attempt < 3; attempt++) { const res = await fetch(url, { headers: { 'User-Agent': UA } }); if (res.status === 429) { await sleep(800 * (attempt + 1)); continue; } if (!res.ok) throw new Error(`FRED release ${releaseId} HTTP ${res.status}`); const data = await res.json(); return (data.release_dates || []).map((row) => row.date); } throw new Error(`FRED release ${releaseId} rate limited`); } export async function fetchFredMacroEvents(start, end) { const key = getFredKey(); if (!key) return []; const events = []; for (const rel of FRED_RELEASES) { try { const dates = await fetchReleaseDates(rel.id, key); for (const date of dates) { if (date < start || date > end) continue; events.push({ date, time: rel.time, title: rel.title, category: 'macro', impact: rel.impact, source: 'FRED releases', note: rel.note, }); } } catch { // 單一來源失敗不阻擋整體日曆 } await sleep(300); } return events; }