// 市場結構日:選擇權結算、休市、Jackson Hole、主要央行利率決議(靜態日程,每年可更新) function iso(y, m, d) { return new Date(Date.UTC(y, m, d)).toISOString().slice(0, 10); } function inRange(date, start, end) { return date >= start && date <= end; } function addEvent(events, ev) { if (!ev?.date || !ev?.title) return; events.push({ time: ev.time || '', impact: ev.impact || 'low', category: ev.category || 'macro', source: ev.source || 'Market calendar', symbol: null, url: ev.url || null, note: ev.note || '', ...ev, }); } function thirdFridayISO(year, monthIndex) { const dow = new Date(Date.UTC(year, monthIndex, 1)).getUTCDay(); const day = 1 + ((5 - dow + 7) % 7) + 14; return iso(year, monthIndex, day); } const QUAD_MONTHS = new Set([2, 5, 8, 11]); export function fetchOptionsExpiryEvents(start, end) { const events = []; const y0 = Number(start.slice(0, 4)); const y1 = Number(end.slice(0, 4)); for (let y = y0; y <= y1; y++) { for (let mo = 0; mo < 12; mo++) { const date = thirdFridayISO(y, mo); if (!inRange(date, start, end)) continue; if (QUAD_MONTHS.has(mo)) continue; // 四巫日由 calendar.js 另列 addEvent(events, { date, time: '16:00 美東', title: '月選擇權結算', category: 'derivatives', impact: 'medium', note: '每月第三個週五,個股與指數選擇權到期,換倉時波動可能加大', }); } } return events; } function nthWeekday(year, monthIndex, weekday, n) { let count = 0; for (let day = 1; day <= 31; day++) { const d = new Date(Date.UTC(year, monthIndex, day)); if (d.getUTCMonth() !== monthIndex) break; if (d.getUTCDay() !== weekday) continue; count++; if (count === n) return iso(year, monthIndex, day); } return null; } function lastWeekday(year, monthIndex, weekday) { for (let day = 31; day >= 1; day--) { const d = new Date(Date.UTC(year, monthIndex, day)); if (d.getUTCMonth() !== monthIndex) continue; if (d.getUTCDay() === weekday) return iso(year, monthIndex, day); } return null; } function observedFixed(year, monthIndex, day) { const d = new Date(Date.UTC(year, monthIndex, day)); const dow = d.getUTCDay(); if (dow === 6) return iso(year, monthIndex, day - 1); if (dow === 0) return iso(year, monthIndex, day + 1); return iso(year, monthIndex, day); } function easterSunday(year) { const a = year % 19; const b = Math.floor(year / 100); const c = year % 100; const d = Math.floor(b / 4); const e = c % 4; const f = Math.floor((b + 8) / 25); const g = Math.floor((b - f + 1) / 3); const h = (19 * a + b - d - g + 15) % 30; const i = Math.floor(c / 4); const k = c % 4; const l = (32 + 2 * e + 2 * i - h - k) % 7; const m = Math.floor((a + 11 * h + 22 * l) / 451); const month = Math.floor((h + l - 7 * m + 114) / 31) - 1; const day = ((h + l - 7 * m + 114) % 31) + 1; return iso(year, month, day); } function goodFriday(year) { const e = easterSunday(year); const d = new Date(e + 'T00:00:00Z'); d.setUTCDate(d.getUTCDate() - 2); return d.toISOString().slice(0, 10); } export function fetchUsMarketHolidayEvents(start, end) { const events = []; const y0 = Number(start.slice(0, 4)); const y1 = Number(end.slice(0, 4)); for (let y = y0; y <= y1; y++) { const holidays = [ [observedFixed(y, 0, 1), '美股休市:元旦', '全美主要交易所休市,流動性偏低'], [nthWeekday(y, 0, 1, 3), '美股休市:馬丁路德金恩日', ''], [nthWeekday(y, 1, 1, 3), '美股休市:總統日', ''], [goodFriday(y), '美股休市:Good Friday', '復活節前一週五,歐美常同步休市'], [lastWeekday(y, 4, 1), '美股休市:陣亡將士紀念日', ''], [observedFixed(y, 5, 19), '美股休市:Juneteenth', ''], [observedFixed(y, 6, 4), '美股休市:獨立紀念日', ''], [nthWeekday(y, 8, 1, 1), '美股休市:勞動節', ''], [nthWeekday(y, 10, 4, 4), '美股休市:感恩節', ''], [observedFixed(y, 11, 25), '美股休市:聖誕節', ''], ]; for (const [date, title, note] of holidays) { if (!date || !inRange(date, start, end)) continue; addEvent(events, { date, title, category: 'market', impact: 'medium', note: note || '交易所休市或提早收盤,留意流動性', }); } const jackson = lastWeekday(y, 7, 5); if (jackson && inRange(jackson, start, end)) { addEvent(events, { date: jackson, time: '美東日間', title: 'Jackson Hole 全球央行年會', category: 'fed', impact: 'high', note: 'Fed 主席與各國央行官員演講,常影響利率與風險資產預期', }); } } return events; } // 官方已公布的 2026 利率決議日(超出範圍者會被 start/end 自動濾掉) const CENTRAL_BANK_MEETINGS = [ { date: '2026-02-05', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-03-19', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-04-30', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-06-11', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-07-23', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-09-10', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-10-29', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-12-17', title: 'ECB 利率決議', source: 'ECB', impact: 'high', note: '歐洲央行貨幣政策決議與記者會' }, { date: '2026-01-24', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議,影響日圓與亞股' }, { date: '2026-03-19', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-05-01', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-06-17', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-07-31', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-09-19', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-10-30', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-12-19', title: '日本央行 利率決議', source: 'BOJ', impact: 'high', note: '日本銀行貨幣政策決議' }, { date: '2026-02-05', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-03-19', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-05-07', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-06-18', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-08-06', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-09-17', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-11-05', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, { date: '2026-12-17', title: '英央行 MPC 利率決議', source: 'BOE', impact: 'high', note: '英格蘭銀行貨幣政策委員會決議' }, ]; export function fetchCentralBankEvents(start, end) { const events = []; for (const row of CENTRAL_BANK_MEETINGS) { if (!inRange(row.date, start, end)) continue; addEvent(events, { date: row.date, time: '依各國時間', title: row.title, category: 'central_bank', impact: row.impact, source: row.source, note: row.note, }); } return events; } export function fetchMarketStructureEvents(start, end) { return [ ...fetchOptionsExpiryEvents(start, end), ...fetchUsMarketHolidayEvents(start, end), ...fetchCentralBankEvents(start, end), ]; }