ETF 成分股顯示名稱 — Frontend Integration Guide
Backend branch: fixed/etf-constituent-display-name → cortex/dev(merge 後 cortex-api --reload 自動生效;cortex-scheduler 不會 hot-reload,merge 後須重啟,否則排程產生的報告仍沿用舊名稱;PR 連結於 merge 後補上)
API base: http://localhost:8000(dev)/ production base 同既有
日期: 2026-10-20
Breaking change: 無(欄位名稱、型別皆不變;只有「值的語意」改變)
1. 改了什麼
ETF 持股相關 API 的 constituent_name 欄位名稱與型別不變(仍是 string | null),值的語意改變:
| 舊 | 新 | |
|---|---|---|
constituent_name 的值 |
各投信回報的原始名稱(同一支股票在不同 ETF 可能不同) | 股票主檔名稱(taiwan.stocks.name) |
| 對不到主檔的成分股 | 投信原始名稱 | 投信原始名稱(不變) |
「對不到主檔」指沒有對應台股代碼的成分股,例如海外股、債券、期貨、現金;以及主檔沒有該代碼、或主檔名稱為空字串的列。這些仍顯示投信回報的原始名稱。
取值規則(後端單一定義):
constituent_name = COALESCE(NULLIF(taiwan.stocks.name, ''), etf_holdings.constituent_name)
JOIN: taiwan.stocks.stock_code = etf_holdings.constituent_stock_code(LEFT JOIN)
資料庫內 taiwan.etf_holdings.constituent_name 的原始值不改,只有對外輸出換成主檔名稱。
2. 為什麼改
同一支股票在不同投信的持股表有不同名稱。2026-10-19 實測 GET /api/etf-holdings/stock-actions/ranking:2454 在同一頁的買超榜第 2 名顯示「聯發科技」、賣超榜第 1 名顯示「聯發科」,使用者看到同一支股票兩個名字。
原因是跨 ETF 彙總時取第一筆遇到的名稱,而 2454 在持股表有「聯發科」「聯發科技」「MEDIATEK INC」三種寫法;股票主檔只有「聯發科」。改為一律輸出主檔名稱後,同一個 constituent_ticker 只會有一種名稱。
3. 受影響的端點
constituent_name 的值語意統一改變的端點(皆在 /api/etf-holdings 之下):
| Endpoint | 欄位位置 |
|---|---|
GET /api/etf-holdings/recap/daily |
etfs[].added[]/removed[]/quantity_changes[] 的 constituent_name(day 與 week 等 period 皆適用) |
GET /api/etf-holdings/stock-actions/ranking |
buy_rankings[]/sell_rankings[] 的 constituent_name |
GET /api/etf-holdings/stock-actions/{raw_code} |
top-level constituent_name(該個股識別資訊) |
GET /api/etf-holdings/{etf_ticker} |
holdings[] 的 constituent_name |
GET /api/etf-holdings/{etf_ticker}/history |
snapshots[].holdings[] 的 constituent_name |
GET /api/etf-holdings/{etf_ticker}/changes |
added[]/removed[]/quantity_changes[] 的 constituent_name |
GET /api/etf-holdings/{etf_ticker}/changes/recent |
stocks[] 的 constituent_name |
GET /api/etf-holdings/{etf_ticker}/estimated-costs |
stocks[] 的 constituent_name |
不受影響:GET /api/etf-holdings/performance(不輸出成分股名稱)。
各端點的欄位表見 docs/api-reference.md 對應段落的 constituent_name 列(說明已統一為「股票主檔名稱;對不到主檔時為投信回報的原始名稱」)。
AI 報告內容
籌碼/週期 recap 與主動式 ETF 日報、AETF 報告的文字內容裡出現的成分股名稱,也改用主檔名稱。這些報告是新產生時才套用新規則,已產生的報告不重產:
- 主動式 ETF 日報、籌碼日報:下一次排程產生的新日期報告起使用主檔名稱(前提:merge 後已重啟 cortex-scheduler——它不會 hot-reload,未重啟則排程報告繼續沿用舊名稱);merge 當下最新一份(2026-10-19)仍是舊內容。
- AETF 報告:使用者按「分析」重新產生後才是新內容。
歷史日期
主檔只存目前名稱。曾經改名的公司,查詢舊日期的持股也會顯示現在的主檔名稱。
4. 前端影響
不需要改程式。 目前前端對 constituent_name 的讀取都只用於顯示;列的 key、分組、排序、跳轉都用 raw_code 或 constituent_ticker,不依賴名稱字串。
注意事項:
- 不要拿
constituent_name當 key、分組鍵或跨端點比對的依據(名稱仍可能因主檔更新而改變,且海外股/債券等沒有主檔的成分股仍是投信原始名稱)。身分比對請用raw_code/constituent_ticker。 - 若有 e2e/snapshot 測試寫死了特定投信的原始名稱(如「聯發科技」),需更新為主檔名稱(如「聯發科」)。
範例(2026-10-19,2454)
{
"raw_code": "2454",
"constituent_ticker": "2454.TW",
"constituent_name": "聯發科",
"asset_type": "stock"
}
買超榜與賣超榜同時出現 2454 時,兩邊的 constituent_name 一致為「聯發科」。對不到主檔的成分股(例如海外股)constituent_name 維持投信回報的原始名稱。
5. 前端遷移 checklist
- [ ] grep 前端 repo 的
constituent_name,確認所有讀取都只用於顯示(key/分組/排序用raw_code/constituent_ticker) - [ ] 更新寫死特定投信原始名稱的 e2e/snapshot 測試期望值
- [ ] 手動驗證:
/stock-actions/ranking(day、week)的buy_rankings與sell_rankings合併後,同一個constituent_ticker只有一種constituent_name - [ ] 手動驗證:
/recap/daily(day、week)各 ETF 的added/removed/quantity_changes中,同一個constituent_ticker只有一種constituent_name - [ ] 不需要改 TypeScript type(
constituent_name: string | null不變)
6. 參考
- Backend branch:
fixed/etf-constituent-display-name - 後端規格:
docs/api-reference.md各/api/etf-holdings端點的constituent_name列 - 相關前端契約: ETF 日期契約