# InvestWatch 分析方法（公開版）

這份文件說明本站「分析」區塊怎麼算、算什麼、每個數字有多可靠。它是分析系列的規格來源，
**只涵蓋 `data/assets.json` 裡的公開標的**，內容全部是方法與定義，沒有任何個人資料。
（原始的方法目錄留在維護者本機，不進公開倉庫。）

> 以上為量化整理，未經回測驗證，不構成投資建議。——這一句固定出現在每一個分析頁面的最下面。

---

## 1. 兩套等級，不要看混

| 名稱 | 標在哪裡 | 意思 |
|---|---|---|
| **方法證據等級 ★** | 只出現在這份文件的「方法」說明裡 | 這個方法在學術與實務上有多站得住：<br>★★★ 反覆驗證、跨市場跨年代都成立，或根本是算術（不需要預測）<br>★★ 有證據，但有條件、會失效、或只在特定尺度成立<br>★ 廣泛使用、名氣大，但證據弱——列出來是讓人知道它為什麼不被採用；用到時標「參考性低」 |
| **資料標籤**（文字，不用星星） | 出現在每一個算出來的數字旁邊 | 這個數字的來源有多可靠：<br>**估算**＝由公式或不同時點的資料推算，不是任何來源直接給的<br>**單一來源**＝只有一個來源，沒有拿別的來源對過<br>**有對照**＝有拿別的來源核對過（核對方法與日期寫在旁邊）<br>**多來源一致**＝兩個以上獨立來源給出一致的值 |

星星只描述「方法」；數字旁邊永遠是文字標籤。兩者不會混在同一個地方出現。

## 2. 誠實規則（程式與頁面都照這幾條）

1. **不把各面向加總成一個數字、不做任何評分。** 每個面向各自獨立表態＋一句理由；同向的面向有幾個就說幾個，決定權在使用者。
2. 每個數字帶**資料截止日期**（哪一週／哪一天）、**視窗長度**、**資料標籤**。
3. 資料不夠就寫「資料不足」，不硬算；估算值一律標「估算」。
4. 每個日期都來自資料來源自己的時間戳（Yahoo K 棒的時間戳、FinMind 回應的 date 欄），絕不用「現在的時間」充當資料日期。
5. 15:30 算分析時，經 FinMind 取得的資料（匯率、本益比、融資、法人）一定是**前一個交易日**的——這不是錯誤，是事實，每個輸出照實標。
6. 抓不到就明確標失敗，不拿舊資料假裝新資料；分析壞掉時 `data/analysis/status.json` 會記錯誤，頁面最上面會顯示。
7. 程式、頁面、文件、輸出檔永遠不出現投資判斷用語；自動檢查（`scripts/test_analysis_guards.py`）擋著。A1-6 起清單多了帶方向與資金動向的字眼（見第 6 節）。
8. 需要「使用者自己的部位」參與的任何計算（集中度）只在瀏覽器裡做，結果不寫回任何公開檔。

## 3. 五個面向的方法清單

為什麼分「面向」：五個技術指標若全部來自同一組近期價格，本質上是同一件事講五遍。把方法按「回答什麼問題」分成五個面向，每個面向只挑 1～2 個代表，同向才真的算得上是多個證據。

### 面向 1｜估值：現在貴不貴？

| 方法 | 看什麼 | 方法證據 | 適用類別 |
|---|---|---|---|
| 到期殖利率（YTM） | 債券持有到到期的年化報酬，幾乎是算出來的 | ★★★ | bond_etf（注意：債券 ETF 不會真的「到期」，見第 5 節） |
| CAPE（席勒本益比） | 用過去 10 年平均盈餘算的本益比 | ★★★（對 10 年後報酬）／★（當進出訊號） | index、index_etf |
| 本益比歷史百分位 | 跟它自己的過去比 | ★★ | stock、index |
| 股價淨值比 | 股價相對帳面資產 | ★★ | stock |
| 殖利率歷史位置 | 配息 ÷ 股價，跟自己歷史比 | ★★ | stock |
| 實質價格（扣通膨） | 扣掉通膨後的真實價格 | ★★★（算術） | gold_tw、commodity |
| 歷史百分位（1／5／10／20 年） | 現價在過去區間的位置 | ★★★（算術） | 全部 |
| 購買力平價（PPP） | 兩國物價推算的理論匯率 | ★★（長期有用、短期沒用） | fx |

### 面向 2｜趨勢動能：現在往哪個方向走？

| 方法 | 看什麼 | 方法證據 | 適用類別 |
|---|---|---|---|
| 10 個月／200 日移動平均 | 價格在均線之上或之下 | ★★★ | index、index_etf、commodity、gold_tw、crypto |
| 時間序列動能（12 個月） | 過去 12 個月的報酬是正還是負 | ★★★ | index、index_etf、bond_etf、commodity、fx |
| 橫斷面動能（12-1） | 一群標的裡，過去 12 個月（扣最近 1 個月）最強的是誰 | ★★★ | 多標的比較 |
| MACD、KD、威廉指標 | 短線訊號 | ★（彼此高度重複，參考性低） | — |

### 面向 3｜情緒籌碼：別人在想什麼、在做什麼？

| 方法 | 看什麼 | 方法證據 | 適用類別 |
|---|---|---|---|
| VIX | 市場預期未來 30 天的波動；極端值才有意義 | ★★ | index、index_etf（美股） |
| 融資餘額 | 借錢買股的總額（台股特有的公開資料） | ★★ | stock（台股）、index（台股） |
| 三大法人買賣超 | 外資、投信、自營商的進出（台股特有） | ★★ | stock（台股）、index（台股） |
| ETF 資金流向 | 發行單位數的變化 | ★★ | index_etf、bond_etf |
| RSI | 近 14 日漲跌力道 | ★（單獨用參考性低，只當輔助描述） | 全部 |

### 面向 4｜基本面動能：體質在變好還是變壞？

| 方法 | 看什麼 | 方法證據 | 適用類別 |
|---|---|---|---|
| 月營收年增率 | 台灣公司每月公布的營收，跟去年同月比 | ★★ | stock（台股） |
| 殖利率曲線 | 短天期利率高於長天期＝倒掛（連最強的指標也會失手，這正是要看多個面向的原因） | ★★★ | 總經、全市場 |
| 毛利率、ROE 趨勢 | 賺錢能力的方向 | ★★ | stock |
| PMI、失業率 | 景氣的領先／同步指標 | ★★ | 總經 |

### 面向 5｜風險：如果錯了，會多痛？

這一面向全部是事實、不含預測，所以方法證據等級自然高；也是最常被忽略的一塊。

| 方法 | 看什麼 | 方法證據 | 適用類別 |
|---|---|---|---|
| 最大回檔（MDD） | 從最高點到之後最低點，最多跌掉幾 % | ★★★ | 全部 |
| 年化波動度 | 價格震盪的幅度 | ★★★ | 全部 |
| 相關係數矩陣 | 標的之間有多常一起漲跌 | ★★★ | 全部，而且跨標的 |
| 集中度 | 有多少部位壓在同一個風險因子上（含收入來源） | ★★★ | 整體（只在瀏覽器計算） |
| Beta | 相對大盤的敏感度 | ★★ | stock、index_etf |

### 橫跨全部｜成本：實際付了多少？

全部是 ★★★（確定的算術），而且長期影響通常大於擇時：ETF 的內扣費用、追蹤差距（常比內扣費用還大）、折溢價；股票的手續費與證交稅；黃金存摺的本行買入與本行賣出之間的價差；實體條塊的溢價；換匯的價差。

## 4. 資產分類（assetClass）與自動套用

`data/assets.json` 每個標的都有 `assetClass`（跟 `type`＝資料來源種類是兩回事）。新增標的只要標對類別，該類別的方法組合就自動掛上。

| assetClass | 意思 | 自動套用 | 目前的公開標的 |
|---|---|---|---|
| `stock` | 個股 | 五面向全開；台股另有月營收、融資、法人 | 台積電 2330（台股）、NVIDIA（美股，沒有月營收與融資） |
| `index` | 指數（參考序列，不是可持有的部位） | 估值、趨勢、情緒、風險 | 台股加權指數、S&P 500 |
| `index_etf` | 廣泛指數 ETF | 估值（CAPE）、趨勢、風險、成本、總經 | 元大 S&P500（00646） |
| `bond_etf` | 債券 ETF | YTM、存續期間、利率預期、成本 | 元大美債 20 年（00679B） |
| `commodity` | 商品（含參考序列） | 歷史百分位、實質價格、趨勢、成本；基本面關閉 | 國際金價（COMEX 近月期貨 GC=F）、WTI 原油 |
| `crypto` | 加密貨幣 | 百分位、趨勢、風險 | 比特幣 |
| `fx` | 匯率 | PPP、利差、百分位、趨勢 | 美元／台幣、人民幣／台幣 |
| `gold_tw` | 台銀黃金（存摺與條塊） | 歷史百分位、實質金價、趨勢、成本（價差、條塊溢價）、拆解 | 黃金存摺台幣、黃金存摺人民幣、實體條塊 |

## 5. 已實作的部分（分析系列 A1-1、A1-2、A1-3、A1-5、A1-6、A1-7）

每天雲端 15:30 的 review 完整更新跑一次 `scripts/analyze.py`，只在那一輪算；盤中的輕量更新完全不碰分析。輸出都在 `data/analysis/`；「分析」分頁 `analysis.html` 列出全部，儀表板每張卡也有一個「分析」摺疊區（A1-5 起；舊網址 `analysis-debug.html` 只做跳轉）。A1-7 起每張卡另有一條面向的圖示列、分析分頁最上面有一張總覽表（見 5.11）。

### 5.1 週線長歷史 `data/history-long/<id>.json`

- 來源：Yahoo Finance chart API 的週線（`period1=345600&period2=<現在>&interval=1wk`；345600 是 1970-01-05 星期一——Yahoo 把「一週」的起點對齊 `period1` 那一天的星期幾，用 0（1970-01-01 是星期四）會讓歷史夠長的 S&P 500 變成週四起的週、跟其他標的差半週），對象是雲端負責且有 Yahoo 代號的標的：加權指數、台積電、00646、00679B、NVIDIA、S&P 500、比特幣、WTI 原油、國際金價。
- 檢查：回應宣告的粒度必須是 `1wk`、K 棒時間戳的中位間距必須是 6～8 天（`range=max` 那種寫法會被無聲地換成月線，一律不收）；**進行中的當週不收**（起始＋7 天 ≤ 抓取當下才算完成）；收盤價空值的週略過。
- 日期：週棒的日期是該週第一個交易日、交易所當地日期；每一點都帶 `dateSource`。
- 更新時機：存檔最後一根的日期早於「最近一個已完成週的週一」（今天所在 ISO 週的週一減 7 天）才補抓，所以每週只在週一那次 review 抓一次；週一沒跑到，週二會自動補上。（A1-1 原本寫「超過 7 天才補抓」，實際上週二到下週一每天都會抓，2026-09-25 修正。）
- USD/TWD：FinMind 轉載的台銀每日牌價（`TaiwanExchangeRate`，2006 年起），每個 ISO 週取最後一個營業日、抓取當週還沒走完所以不收；檔頭標「原始出處：臺灣銀行牌告匯率（經 FinMind 取得）」。它的資料標籤是「有對照」——2026-09-18 逐日稽核 136 天，轉載值與台銀 CSV 完全一致。
- 人民幣／台幣（A1-6）：做法跟 USD/TWD 完全相同（FinMind `TaiwanExchangeRate` 的 CNY，每個 ISO 週取最後一個營業日、不收進行中的週、資料標籤「有對照」）。來源從 2006 年起就有列，但 2013 年以前的列全部是 −1（缺值）：剔除後有效資料自 2013-01-02，檔頭照實記來源列數、有效列數與剔除列數。
- 美元兌人民幣 `usdcny.json`（A1-6；Yahoo `CNY=X` 週線）：只給人民幣拆解用，不是監控標的、不在 assets.json、不進風險表與相關矩陣。
- 00679B 只有 2017-01 起，10 年視窗到 2027-01 才夠；在那之前所有 10 年視窗都顯示「資料不足，2027-01 起才滿 10 年」。

### 5.2 風險 `data/analysis/risk.json`

| 項目 | 定義 | 視窗 | 資料標籤 |
|---|---|---|---|
| 年化波動 | 週報酬（該週完成週棒收盤 ÷ 前一週 − 1）的樣本標準差 × √52 | 52 週與 260 週；視窗內缺超過 10% 就資料不足 | 單一來源（匯率：有對照） |
| 最大回檔 | 整段可用區間內，從歷史高點到之後最低點的跌幅；附高點、低點與回到高點的日期 | 各檔自己的整段歷史（起點不同） | 同上 |
| 目前距高點 | 最後一根完成週棒距整段歷史高點的跌幅 | — | 同上 |
| 相關係數矩陣 | 皮爾森相關，週報酬、ISO 週對齊、**成對可用**（每一對各自取兩邊都有的最近 156 週） | 3 年；重疊不到 100 週寫資料不足 | 單一來源 |

A1-6 起矩陣是 12 個序列（多了人民幣／台幣）；人民幣／台幣也有風險三項。

幣別混用要知道：台股與匯率是台幣、美股與商品是美元、比特幣是美元計價；相關係數是各自幣別的報酬，不含匯率換算。矩陣的對角線必須是 1，算出來不是 1 會被記成問題、整張矩陣標為不可信。

### 5.3 拆解 `data/analysis/decompose.json`（全部是估算）

- **台銀金價**：台銀黃金存摺的本行賣出價（台幣／公克）≈ 國際金價（美元／盎司）× USD/TWD ÷ 31.1035 ＋ 殘差。1 金衡盎司＝31.1035 公克。兩個口徑都算：同一天的 GC=F 收盤（事後對照；美國收盤時台北已是隔天早上）、前一個交易日的 GC=F 收盤（台銀掛牌時實際看得到的最新收盤）。殘差＝台銀的隱含價差＋期貨與現貨的基差＋時間差。匯率用即期買入與即期賣出的中價。
- **00646**：月報酬 ≈ (1＋S&P 500 月報酬)(1＋USD/TWD 月變動) − 1 ＋ 殘差。月收盤用週線長歷史裡起始日落在該月的最後一根近似（跟真正月底最多差 4 天）；還沒走完的當月不算。S&P 500 用價格指數、00646 不配息，所以殘差裡含股息效果（約每年 +1%）、費用、期貨替代、匯率時點差；殘差的累計是追蹤差距的近似值。

- **人民幣兌台幣**（A1-6）：台幣兌人民幣 ≈ 台幣兌美元 ÷ 美元兌人民幣 ＋ 殘差。台幣兌美元、台幣兌人民幣用台銀牌告即期買入與即期賣出的中價（每週最後一個營業日），美元兌人民幣用 Yahoo `CNY=X` 的週線收盤；ISO 週對齊、三條序列都有的週才算。輸出 1 年（52 週）與 3 年（156 週）的變動各自拆成兩塊——美元對台幣怎麼動、美元對人民幣怎麼動——合成＝(1＋美元兌台幣的變動) ÷ (1＋美元兌人民幣的變動) − 1，殘差＝實際 − 合成；另列最近 52 週每週殘差的中位數與範圍、最近 26 週的逐週明細。殘差來自兩個來源的報價時點不同（台銀是台北營業時間的牌價、Yahoo 是國際市場的週收盤）與台銀自己的訂價。

### 5.4 狀態 `data/analysis/status.json`

每次執行都寫：上次執行時間、有沒有全部產出、錯誤清單、每個長歷史這次做了什麼、對外請求次數。不含任何機器名稱與路徑。檢視頁最上面就是它。

### 5.5 成本 `data/analysis/cost.json`（分析系列 A1-2）

| 項目 | 定義 | 視窗 | 資料標籤 |
|---|---|---|---|
| 00646 追蹤差 | 累計差＝00646 台幣報酬 − [(1＋基準美元報酬)(1＋USD/TWD 變動) − 1]；年化差＝(1＋00646 報酬) ÷ (1＋基準台幣報酬) 開 n 次方 − 1（幾何）。基準主口徑 ^SP500TR（S&P 500 總報酬指數，含股息再投資），對照口徑 ^GSPC（價格指數；兩者的差距大約就是股息）。匯率用即期買入與即期賣出的中價。週線、ISO 週對齊；終點是三條序列共同的最後一根完成週棒，起點往前數 52／156 週、那一週缺了就再往前找最多 3 週；每個視窗都輸出實際的起訖日期 | 1 年（52 週）、3 年（156 週） | 估算（00646 不配息、費用含在淨值裡；基準是指數，不是可投資的東西） |
| ETF 折溢價（00646、00679B） | 折溢價＝(價格 − 淨值) ÷ 淨值 × 100。兩個口徑分開存、分開標：**預估**＝證交所 all_etf.txt 那一筆的成交價對投信盤中預估淨值（檔內的日期與時間照抄）；**確定**＝前一營業日收盤價對官方淨值（官方淨值隔天才拿得到，回填到它所屬的那一天）。每個交易日一列、最多留 400 列（`data/analysis/nav/<id>.json`），累積滿 20 個交易日才顯示中位數與範圍；A1-7 起摘要另帶自己歷史的第 25／75 百分位（`p25Pct`／`p75Pct`，線性內插，一樣滿 20 個交易日才有），給頁面判斷「現在的折溢價在自己的歷史裡算低、算中間、還是算高」 | 從 2026-09 開始累積 | 預估：估算；確定：單一來源 |
| 黃金存摺價差 | (本行賣出 − 本行買入) ÷ 中價 × 100，每日一筆；顯示最新一筆與整段的中位數、範圍。台幣計價與人民幣計價（A1-6）兩張存摺各算各的 | 既有日線歷史（約 260 天） | 單一來源 |
| 實體條塊溢價 | (整條牌價 ÷ 公克數) ÷ 存摺本行賣出 − 1，各規格（1 公斤、500／250／100 公克、金鑽 1 台兩＝37.5 公克）每日一筆 | 既有條塊歷史（2026-09-25 只有 12 天，天數照實寫） | 單一來源 |
| 靜態費用表 `data/analysis/static-costs.json` | 手抄自官方頁面：兩檔 ETF 的經理費與保管費分級費率、證券交易稅條例的稅率與債券 ETF 停徵期限等；每一筆都帶出處網址、查核日期與標籤「官方公告」；讀不到或有歧義的欄位留 null 並寫原因 | — | 官方公告 |

- all_etf.txt 每天約 17:00 才更新一次，15:30 那一輪拿到的是前一個交易日那一筆（2026-09-25 實測：檔內日期欄 20260924、時間欄 17:00:00）。所以每天存進 nav 的是前一交易日的「預估」列，官方淨值再往前一天回填；日期照檔內欄位抄，標籤沒有錯，只是每一列晚一天到。
- 淨值來源（證交所 all_etf.txt）在 A0 探測時曾在雲端被「安全性考量」頁擋下；A1-2 改用正式抓法（同一個連線、帶 Referer）重測後可用。被擋時 cost.json 寫「未接」而不是拿舊資料充數；00679B 另有櫃買中心最近 30 個交易日的官方折溢價當退路（標「僅 30 日」）。
- 黃金現貨（XAUUSD=X、XAU=X）在 Yahoo 已抓不到（404），拆解裡的殘差仍含期貨基差；為什麼殘差常常是負的，decompose.json 的 notes 有解釋（期貨價＝現貨＋融資成本 − 租賃收益，近月合約通常比現貨高）。
- 追蹤差的基準序列 `data/history-long/sp500tr.json` 跟其他長歷史一樣一週補一次；它不是監控標的、不在 assets.json。

### 5.6 集中度（只在瀏覽器裡算；分析系列 A1-2）

- 設定檔 `analysis-profile.json` 放在使用者自己的私人倉庫（跟個人紀錄同一個地方），內容只有：八個 assetClass 的權重百分比（stock、index_etf、bond_etf、commodity、crypto、fx、gold_tw、other）、一個「收入來源代理標的」（從公開清單挑一個標的代表自己的收入所屬產業）、一個月份。沒有任何金額、數量、名稱。
- 三個事實：最大單一類別權重；前二類合計；與收入來源代理同向的權重合計——各類別用代理標的（stock→台積電、index_etf→00646、bond_etf→00679B、commodity→國際金價、crypto→比特幣、fx→美元／台幣、gold_tw→國際金價）對收入代理的 3 年週報酬相關係數判斷，門檻 0.5，表格顯示的是相關係數本身；`other` 併入最大權重與前二合計、不納入同向計算（相關性未知，會照實寫）。相關係數直接讀 risk.json，瀏覽器不重算。
- 檢視頁的表單只在「私人倉庫」模式下能用：先測連線再存；存檔的 commit 訊息只有「analysis-profile 更新（時間）」，不含任何數字；負數與非數字擋下、總和不在 95～105 只警告。表單欄位的 id／name 跟設定檔的鍵名不同；公開輸出、頁面靜態內容、畫出來的 DOM 都有測試掃過，設定檔的鍵名一個都不會出現；權重不寫進 console、不寫進任何公開檔。
- 沒有設定檔就顯示「未設定」與一份空白範本；結果只顯示、不上傳。

### 5.7 資料條款

- Yahoo：非官方 API、個人用途；存的是週收盤衍生值，跟本站既有的日線做法一致。
- FinMind：只有 FinMind 拿得到的原始資料不整批存進公開倉庫、只存算出來的指標值；底層是官方公開來源（台銀牌價、證交所、櫃買）的可以存必要的歷史，並標原始出處。
- FRED（A2 起會用到）：條款禁止封存，所以現抓、現算、只存當次算出的指標與結論。
- 證交所 all_etf.txt：官方公開資料，存必要的歷史（每日一列的折溢價累積）並在檔頭標原始出處；櫃買中心的 30 日折溢價只存當次算出的摘要。
- 靜態費用表：內容抄自官方頁面（投信官網、全國法規資料庫），每一筆帶網址與查核日期，不轉載頁面全文。
- 試算（A1-3）：Yahoo 個人用途；結果只存在使用者自己的私人倉庫，公開倉庫不存任何代號或結果。

### 5.8 試算清單（分析系列 A1-3；計算程式公開、代號與結果私人）

- **目的**：臨時分析一支不在正式清單上的標的（例如海外券商上的某支 ETF），不必長期追蹤，公開倉庫不留任何痕跡。
- **架構**：公開倉庫的 `scripts/analyze.py` 開單一標的入口 `--adhoc 代號 --asset-class 類別 [--expense-ratio 值] --out 倉庫外目錄`，沿用 Yahoo 週線的抓法與檢查（週一對齊、粒度與間距、不收進行中的週）。它只在使用者**私人倉庫**的 Actions 裡跑（範本 `docs/adhoc-workflow.example.yml`：私人 workflow 只有 `contents: write` 權限；checkout 公開倉庫 main 唯讀、不留 credential）；結果寫到私人倉庫 `adhoc/<代號>/<執行日>.json`，並更新 `adhoc/index.json`（每個代號的最新一筆與幾個摘要數字，網站先讀它、1 個請求）。同一天重跑覆蓋同一檔，舊檔全部保留。
- **結構性禁寫**：`--out` 落在倉庫目錄裡就在連網之前結束（結束碼 2）；adhoc 模式下所有寫檔函式都要經過「允許寫入根目錄」的檢查，路徑組錯也寫不進倉庫；adhoc 的 `status.json` 也只寫到 out。公開倉庫的 workflow 有測試釘住不得出現 `--adhoc`。
- **算什麼**：年化波動 1／5 年、最大回檔、目前距高點（跟 risk.json 同一套規則）；與全部公開標的的 3 年週報酬相關係數並列出相關最高的前三名（回答「它跟我已經有的東西像不像」）。估值與折溢價一律「資料不足」；費用率由使用者輸入、標「使用者輸入」；固定註記「註冊地造成的稅務差異（股息預扣稅、遺產稅）本系統不計算，需另查最新規定」。A2 完成後試算會自動多趨勢面（因為 checkout 的是公開倉庫 main）。
- **必須處理**：倫敦掛牌的報價常是便士——Yahoo 的 `meta.currency` **完全等於** `GBp` 才 ÷100 並記錄「原始報價是便士」，`GBP`（英鎊）與其他幣別不動；幣別照 meta 記錄，相關係數用各自幣別；上市未滿 3 年 → 相關與 5 年波動「資料不足」；代號查無 → 明確失敗、不產生空結果；代號、結果、commit 訊息都不出現在公開倉庫與公開 Actions 紀錄。
- 類別下拉七類：stock／index_etf／bond_etf／commodity／crypto／fx／index，不含 gold_tw（台銀黃金沒有 Yahoo 代號）。

### 5.9 儀表板整合（分析系列 A1-5，第一版）

- **卡片層**：每張可展開的資產卡多一個預設收合的「分析」摺疊區，有什麼顯示什麼：年化波動 1／5 年、最大回檔（高點、低點、回到高點的日期）、目前距高點，最同向（r 最高）、最不相關（|r| 最接近 0）、反向最強（只在 r < −0.3 時顯示；分散與對沖分開列；A1-7 起，S&P 500 指數與它的總報酬版本 sp500tr 不互相當「最同向」——兩者是同一個東西的兩個版本、相關是 1.00，列出來沒有資訊，取下一個），成本有才顯示：00646 追蹤差（主口徑 3 年年化）、折溢價（預估／確定各一筆，累積不到 20 個交易日標「累積中 n/20」）、黃金存摺價差、條塊溢價。每個數字旁有資料標籤與資料日期。「目前距高點」畫一條小橫條，刻度從 0 到該標的歷史最大回檔——那是位置，不是評分；橫條的日期是最後一根完成週棒那天，跟卡片上方的日線報價日期並列印出來。
- **頁面層**：`analysis.html`：分析狀態列（status.json）、風險總表（可按欄排序，資料不足排最後）、相關矩陣（負相關藍、接近 0 中性、正相關橙；格內數字才是訊息、顏色只是輔助；手機可橫向捲動）、成本、拆解、集中度（同一套 `js/concentration.js`）、試算（A1-3 的區塊搬過來）。
- **懶載入**：儀表板開頁不抓任何分析檔，第一次展開卡片的分析區才抓 risk.json（需要成本項的卡再抓 cost.json——A1-5 是四張，A1-6 加了人民幣存摺共五張）；decompose.json 與 history-long 完全不由儀表板載入。（A1-7 改成「首屏畫完之後才抓」，抓的檔案多一個 fx.json，見 5.11；decompose.json 與 history-long 仍然不載。）
- **一項不減**：儀表板原有的報價、走勢、燈號、報告入口、歷史、我的紀錄、設定一個不動；改前改後的 DOM 比對只有新增行，燈號邏輯用改程式之前產生的 fixture 釘住。
- 沒有週線長歷史的四張卡（台銀黃金 3 項、人民幣匯率）只列成本項；什麼都沒有的卡寫「這個標的目前沒有分析項目」。（這是 A1-5 當時的狀態；A1-6 已補：人民幣存摺有自己的存摺價差，人民幣匯率有了週線長歷史所以風險三項與相關列自動出現，另外多一個「換匯助手 →」入口。）

### 5.10 匯率與換匯助手（分析系列 A1-6）

**資料**

- 人民幣的週線長歷史與美元兌人民幣的週線：見 5.1。
- 匯率日線 `data/history/fx_usd.json`、`fx_cny.json`：原本只有 2026-03-02 起約 145 點，2026-09-27 用 `scripts/backfill_fx_history.py` 一次回補到 400 個營業日。規則：只增不改（只新增比現有第一天更早的日期，現有的點一個欄位都不動、`dateSource` 不改寫）；重疊的日子逐日比對即期買入與即期賣出，任何一筆差超過 0.3% 就停下來、什麼都不寫；新補的點標 `dateSource=finmind`。那一次的結果：兩個幣別重疊 145 天完全一致。

**`data/analysis/fx.json`（公開；沒有任何個人的數字）**——美元、人民幣各一組：

| 項目 | 定義 | 視窗 | 資料標籤 |
|---|---|---|---|
| 百分位 | 視窗裡小於或等於「最新一筆日線即期賣出」的比例 | 1 年＝最近 252 個交易日的日線，不到 227 點就資料不足；5 年＝260 週、10 年＝520 週的週線，不到九成就資料不足 | 有對照 |
| 距 1 年低點／高點 | 最新即期賣出 ÷ 1 年視窗的最低（最高）− 1；附最低與最高的日期 | 同 1 年視窗 | 有對照 |
| 即期價差 | (即期賣出 − 即期買入) ÷ 中價 × 100；另列 1 年視窗的中位數、最小、最大 | 最新一筆；1 年 | 有對照 |
| 現鈔價差 | (現金賣出 − 現金買入) ÷ 中價 × 100 | 只有當天（日線歷史不存現鈔牌價） | 有對照 |
| 現鈔比即期貴多少 | 現金賣出 ÷ 即期賣出 − 1；兩個價必須是同一天的牌價，不是就寫資料不足 | 只有當天 | 有對照 |

固定一句：「網銀或大額換匯的優惠不在計算內」。

**規則表（分批表；公開、寫在頁面上）**——輸入是人民幣的 5 年百分位，比例是對「預算池」算的：

| 5 年百分位 | 本月額度（佔預算池的比例） |
|---|---|
| < 20% | 100% |
| 20–40% | 60% |
| 40–60% | 40% |
| 60–80% | 20% |
| > 80% | 0%（觀望） |

邊界：剛好 20、40、60 算較高的那一檔；剛好 80 算 60–80%。1 年百分位並列顯示但不進規則（1 年太短）。頁面上的措辭是「規則試算：本月額度 預算池的 X%」。

**預算池模型（回測與本月試算用同一套）**

- **預算池**：每個月把月預算放進池子；池子＝到目前為止累積的預算 − 已經換掉的台幣。沒換的錢留在池子裡，之後便宜時可以一次多換——這才是「貴時少換、便宜時多換」的意思。
- **規則額度**＝規則比例 × 池子（不是 × 月預算）。
- **保底**（有設期限才有）＝池子 ÷ 剩餘月數（兩頭都算）；同時有設目標總額時，再跟「還差的人民幣 × 現在的即期賣出 ÷ 剩餘月數」比，取較大者。還差的人民幣＝目標總額 − 已換人民幣；已換人民幣＝每一筆紀錄的台幣 ÷ 當時的匯率，加總，所以每一筆紀錄的匯率必填。
- **本月額度**＝max(規則額度, 保底)，**不得超過池子**（手上沒有的錢不能換）。規則額度、保底、本月額度三個數字都印出來。
- 預設做法有兩種：A 固定分批（每月換一個月預算）、B 依位置調整（上面的規則額度）；兩種都算、都印，哪一種當預設由下面的歷史模擬裁決，不是使用者選的。預設是 A 時，本月額度＝max(一個月預算, 保底)，一樣不超過池子。

**本月試算（只在瀏覽器裡算）的兩個細節**

- 池子從設定檔的「計畫起始月」開始累積；計畫起始月以前的已換紀錄不從池子扣（那時候池子還沒開始），但算進已換人民幣。
- 本月額度用「月初的池子」算——這個月的預算已經放進去、這個月換掉的還沒扣；這個月已經換的，再從算出來的額度裡扣一次，頁面另外印「本月還沒換的額度」與「現在的池子」。要是改用扣掉本月已換之後的池子去乘比例，換完本月額度再回來看，頁面會叫人把剩下的池子再乘一次比例、一直換下去。

**歷史模擬（回測）**——資料標籤「歷史模擬」：拿歷史資料照規則重演一遍算出來的，不是實際發生過的交易。

- 資料：人民幣週線長歷史的即期賣出（2013 年起）。每個月的第一根週棒是決策日。
- 不偷看未來：決策日的 5 年百分位只用那一天（含）之前的 260 根週棒算；不夠 234 根的月份不當決策日。
- 視窗：每個月起算一個 36 個月的視窗（逐月往後移）。
- 兩種做法（月預算當一個單位）：A「每月固定換一個月預算」；B「預算池＋期限保底」——每月換 max(規則比例 × 池子, 池子 ÷ 剩餘月數)，不超過池子；期限＝視窗結束，最後一個月保底等於整個池子，所以一定換完、總額跟 A 一樣，才比得公平。每個視窗各算兩種做法「平均換到的匯率」，改善＝(A 的平均匯率 − B 的平均匯率) ÷ A 的平均匯率，正值表示 B 換到的人民幣比較便宜。
- 純 B（不保底：每月換 規則比例 × 池子）另外列：沒換完預算的視窗比例、花掉的預算比例、它的平均匯率——花的錢比較少，不能直接跟 A 比。
- **裁決寫死在程式裡**：主要比較裡 B 換得比較便宜的視窗 ≥ 55% 而且中位數改善 ≥ 0.5%（大約一次即期價差）才預設顯示 B；否則預設顯示 A「固定分批」，並把數字列出來說明為什麼。
- **這些視窗逐月往後移、彼此大量重疊，不是獨立樣本；勝率的參考價值要打折。** 同一次大幅波動會同時落在很多個視窗裡、被重複計算。
- **贏得多的視窗來自哪裡**：改善達到裁決門檻的視窗，各自找出「單月換最多」的那個決策日，再按日期歸戶。日期都一樣，就表示這些視窗贏的是同一次事件、只是被重複計算。頁面在裁決的下面用一句白話講出來——勝率、中位數、跟換一次的即期價差比、贏得多的那幾次是不是同一次事件、所以預設哪一種；那一句由程式照數字套模板產生，事件不只一次時會照實說「分散在幾個不同的月份」。
- 只在每週一那一輪重算，其餘天沿用上一次的結果（檔案裡照實寫是哪一天算的）；結果裡記著模型的名稱與格式版本，模型或格式換了就不沿用舊結果、當天重算。
- 沿革：A1-6 第一版的主要比較是「規則比例 × 月預算、保底＝剩餘 ÷ 剩餘月數」。總預算＝月預算 × 月數時，保底每個月都剛好等於月預算，B 永遠等於 A，比不出差別；驗收時改成上面的預算池模型。

**私人設定 `fx-plan.json`（只放在使用者自己的私人倉庫）**

- 內容：版本、幣別（人民幣）、計畫起始月（年月，必填；預算池從那個月開始累積）、每月預算（台幣）、目標總額（人民幣，選填）、期限（年月，選填）、已換紀錄（每一筆：月份、台幣、當時的匯率）。沒有任何日期戳欄位。欄位的鍵名只寫在 `js/fxplan.js`。
- 分析分頁按一下才從私人倉庫讀進瀏覽器；沒有設定就顯示「未設定」，位置、成本、規則表、歷史模擬照樣看得到，只是不算金額。
- 表單只在「私人倉庫」模式且有同步金鑰時能用：先測連線再存；存檔的 commit 訊息固定是「fx-plan 更新」，不含任何數字。表單欄位的 id／name 一律 `fxp_` 開頭，跟設定檔的鍵名不同；金額不寫進主控台、不寫進任何公開檔、不出現在任何 HTML 屬性（輸入框自己的值除外）。
- 公開倉庫的程式、資料、測試用的假資料、commit 訊息都不出現真實的計畫起始月、預算、目標、已換金額、期限；測試用的假數字是一看就是假的整數。

**固定註記兩句**（換匯助手永遠印在最下面）：

- 人民幣匯率受人民銀行每日中間價管理，政策影響大，依歷史資料訂的規則可靠度低於股票
- 以上為量化整理與歷史模擬，未經實盤驗證，不構成投資建議

**卡片上「52 週」的說法（所有卡片一體適用）**

- 區間位置用最近 252 個交易日的日線算；這個算法 A1-6 一個字都沒改（A1-5 釘住的燈號 fixture 繼續綠）。
- 改的只有稱呼：資料滿 227 個交易日（252 的九成）才叫「52 週」；不滿就照實寫「近 n 個交易日」並標「資料自 <起日>」。門檻只有一個數字，寫在 `js/indicators.js`，卡片標題、規則句、燈號的提示文字都從那裡拿。（A1-5 以前不滿一年時寫的是「近 N 個月」，那是把天數除以 21 四捨五入來的，不夠精確。）

### 5.11 小白呈現框架（分析系列 A1-7）

把第 6 節的呈現原則從換匯助手推到全站。**純呈現**：不多算任何新指標、不改排程、不加對外請求；原本畫面上的東西一項不減。後端只多了 5.5 提到的那兩個百分位欄位。

**卡片的圖示列（儀表板）**

- 每張卡在標題列與展開區之間多一條：**位置、風險、成本**各一個圖示＋狀態詞，收合時就看得到。它自成一條、不放進標題列——標題列整塊是「看走勢」的按鈕，按鈕裡不能再放按鈕。點某個圖示，就在那一條下面展開那個面向的細節：這次是哪個數字、判定規則、每個數字與它的一行白話、資料日期；一次只開一個。
- 三個面向怎麼判定（門檻在 `js/plain.js`、「哪張卡的哪個面向看哪個數字」在 `js/card-analysis.js`）：

  | 面向 | 看什麼 | 三個狀態詞 | 規則 | 圖示 |
  |---|---|---|---|---|
  | 位置 | 卡片原本的燈號（日線、最近 252 個交易日的區間位置；`js/indicators.js`，A1-5 釘住） | 相對低檔區／中性／相對高檔區 | 低於 25%／25%～75%／高於 75% | 色點（跟燈號同一組顏色） |
  | 風險 | 年化波動 1 年相對 5 年（週線） | 平靜／正常／劇烈 | 低兩成以上／其餘／高兩成以上 | 天氣（晴／多雲／雨） |
  | 成本（存摺價差、條塊溢價、換匯的即期價差） | 最新一筆相對自己的中位數 | 便宜／正常／偏貴 | 低一成以上／其餘／高一成以上 | 天氣 |
  | 成本（ETF 折溢價） | 最新一筆「確定」口徑相對自己歷史的第 25／75 百分位 | 便宜／正常／偏貴 | 低於第 25 百分位／25–75／高於第 75 | 天氣 |

  - **位置不重算**：狀態詞就是燈號函式輸出的那幾個字，圖示列只是把它再擺一次並附上細節。位置用色點而不用天氣，因為低檔不等於好。「偏便宜／中間／偏貴」只用在匯率位置（換匯助手，5 年百分位）與之後的估值（A3），不用在位置。
  - **折溢價為什麼不跟中位數比**：折溢價會是負的（折價），「比中位數高一成」這種比法在負數上沒有意義，所以改跟自己歷史的第 25／75 百分位比。口徑先用「確定」（資料標籤「單一來源」；預估口徑是「估算」），預估並列在細節裡；要不要換口徑等 A1-4 的裁決。
  - 條塊的成本看 1 公斤那一列；存摺與條塊的中位數是「手上有的天數」算的，規則句照實寫天數。
- **三種「沒有狀態」要分清楚**：
  - **資料不足**＝之後會有、現在還不夠：虛線圓圈＋原因。例：台銀黃金三張卡沒有週線長歷史，風險是資料不足；條塊的位置是資料不足（台銀不公布條塊歷史牌價，維持不掛燈號）；折溢價與條塊溢價不滿 20 個交易日時寫「資料不足　累積中 n/20」。
  - **不適用**＝這一類標的沒有這個面向：指數、股票、加密貨幣、商品的成本。卡片上不畫這個圖示；總覽表用灰字寫「不適用」並在細節寫原因。
    不適用說的是沒有「比平常貴或便宜」可比的**動態**成本，不是沒有成本：靜態費用表（5.5）裡有這個標的的項目時（例如台積電的證券交易稅），總覽表展開的細節會把它列出來，帶出處標籤與查核日期；它是固定的費用，所以沒有狀態。卡片不載靜態費用表（首屏之後只多五個請求），所以卡片上看不到這一段。
  - **暫時讀不到**＝分析檔沒抓到（不是資料不足）：圖示寫「暫時讀不到」，下面一行「分析資料暫時讀不到」；白話層本身載不到時，整條只寫那一句。「分析」摺疊區一樣照實寫。
- **匯率卡**的位置只掛 52 週燈號；點開位置的細節，裡面多一行「5 年位置：偏貴（見換匯助手）」並連到 `analysis.html#fx`。
- 之後 A2／A3 要加趨勢、情緒、估值：在 `js/card-analysis.js` 的面向清單加一個名字與一個判定函式，卡片與總覽表就會自己多一格。
- **幾個圖示各看各的**：沒有任何把它們加起來、或數一數有幾個怎樣的程式與文字。

**資料載入（儀表板）**

- 首屏的請求跟以前一樣：7 個靜態檔＋行情與報告各 1 個＋每個標的 1 個日線檔（現在 14 個標的，所以是 23 個）。
- 白話層（`js/plain.js`）、名詞解釋（`js/glossary.js`）與 `data/analysis` 的三個檔（risk、cost、fx）要等**首屏畫完**——行情畫好、每張卡的日線都到了、燈號亮了——才去抓，最多多這 5 個；圖示列先留一行「讀取中」的位置，抓到之後補上。三個檔各抓一次，圖示列與「分析」摺疊區共用；點開卡片、展開摺疊區不會再多抓。
- 這件事有一支測試真的開瀏覽器量：`scripts/test_dashboard_requests_js.py` 自己起一個只聽本機的小伺服器，拿瀏覽器記的請求時間與伺服器收到的順序兩份紀錄互相對照。

**指標格的名詞（儀表板）**

- 卡片展開後的 RSI、MA20／MA60、近 10 日漲跌、年化波動 (30日)、52 週區間位置，標題變成可以點的名詞（點一下展開一句話的解釋）；標題的字與下面原本的小字都沒有改。名詞解釋是首屏之後才載入的：在那之前畫出來的標題先印同樣的字，載入後換成可以點的。

**分析分頁**

- **最上面一行是分析狀態**（上次執行時間、這一輪有沒有錯），接著是**總覽表**：每個標的一列——名稱與分組、現價、位置的資料日期（日線）——每個面向一格（圖示＋狀態詞）。狀態跟卡片上的圖示列是同一份判定；為了用同一個燈號函式，這一頁多讀 `data/latest.json` 與每個標的的日線（這一頁沒有請求數的限制）。完整的「分析狀態」那一段緊接在總覽表下面，內容沒變。
- 預設順序＝儀表板的分組與順序。點「位置／風險／成本」的欄位名稱照那一欄排序：三態照低、中、高（再點一次倒過來）、同一個狀態照名稱；**沒有狀態的（資料不足、暫時讀不到、不適用）永遠排最後**，升冪降冪都一樣。「回到預設順序」恢復原狀。
- 點一列展開那個標的每個面向的數字、規則、白話；風險的週線日期、成本各自的日期都寫在細節裡。手機上整張表橫向捲動、第一欄固定，展開的細節貼著看得到的那一段。
- **這張表不顯示任何計數或加總**（沒有「幾個標的在高檔」之類的字，也沒有合計列）。
- **既有各區**：每個數字下面一行白話；表頭與小標的名詞可以點（點名詞不會觸發排序）。兩個例外：**相關矩陣**一格一句會有一百多句，改成矩陣下面每個標的一句（最同向、最沒有關係、有沒有常常反方向的）；**逐日／逐週／逐月的明細表**只列數字，白話寫在上面的摘要。靜態費用表只有「單一個百分比」的列寫白話，分級費率是一段文字、不是一個數字。
- **試算結果**多一條圖示列：風險（跟卡片同一條規則）＋跟現有標的最像的三檔；兩格各看各的。試算沒有日線所以沒有位置，也沒有成本的歷史可以比。

**白話模板與名詞**

- 模板都在 `js/plain.js`（位置、價差、跟平常比、波動、1 年相對 5 年、最大回檔、目前距高點、相關、追蹤差、折溢價與它在自己歷史裡的排名、累積中、條塊溢價、拆解的殘差、人民幣變動的兩段、費用、集中度的三個事實……）。每一種模板至少三組測試：正常、極端（正負號、0、很大）、資料不足；資料不足一律輸出「資料不足」。白話只說數字的意思，不說該怎麼做。
- 名詞解釋新增：面向、狀態詞、保底、視窗重疊（預算池、歷史模擬 A1-6 已有），以及 RSI、移動平均、近 10 日漲跌、年化波動、區間位置、目前距高點、殘差、淨值、資料標籤、條塊溢價。

**隱私**：這一輪畫的東西全部來自公開的市場資料。集中度那三句白話只有句型寫在公開的程式裡，數字仍然只在瀏覽器裡、從使用者自己的私人倉庫讀進來算，不寫回任何公開檔。

## 6. 呈現原則（「一眼看懂」；分析系列 A1-6 起）

為什麼要有這一節：表格對不熟的人來說很難讀。這一節不改變第 2 節的誠實規則，而是在數字上面加一層「翻譯」。A1-6 的換匯助手是第一個照這樣做的畫面；A1-7 已經把它推到所有卡片與整個分析分頁（做法見 5.11）。

1. **每個面向一個三態圖示＋一個狀態詞，圖示旁邊永遠印出判定規則與這次的數字。** 例：「5 年百分位 72.0% → 偏貴；規則：5 年百分位低於 40% 算偏便宜、40–60% 算中間、高於 60% 算偏貴」。狀態詞用該面向自己的語言，絕不用「偏買／偏賣／主力／建議」這四個詞（自動檢查擋著）：

   | 面向 | 三個狀態詞 | 判定規則 | 何時有 |
   |---|---|---|---|
   | 位置 | 相對低檔區／中性／相對高檔區 | 卡片的燈號：區間位置低於 25%／25%～75%／高於 75%（日線、最近 252 個交易日） | 原本就有的燈號；A1-7 擺進圖示列與總覽表 |
   | 匯率位置 | 偏便宜／中間／偏貴 | 5 年百分位低於 40%／40–60%／高於 60% | A1-6（換匯助手） |
   | 估值 | 偏便宜／中間／偏貴 | A3 定 | A3 |
   | 成本 | 便宜／正常／偏貴 | 價差與溢價：現在的值相對自己的中位數，低一成以上／其餘／高一成以上；ETF 折溢價：相對自己歷史的第 25／75 百分位，低於第 25／25–75／高於第 75 | A1-6（換匯的即期價差）；A1-7（存摺、條塊、折溢價） |
   | 風險 | 平靜／正常／劇烈 | 1 年波動相對 5 年波動：低兩成以上／其餘／高兩成以上 | A1-7 |
   | 趨勢 | 向上／持平／向下 | A2 定 | A2 |
   | 情緒 | 冷／中性／熱 | A3 定 | A3 |

   位置、匯率位置、估值是三件不同的事，所以分三列：位置只說「現在在這段期間的高低哪裡」，不說便宜或貴；「偏便宜／偏貴」這組詞只給匯率位置與估值用。

   風險、成本、匯率位置的圖示用天氣隱喻（晴／多雲／雨）；**位置用色點、不用天氣**——低檔不等於好，不該配一個晴天。資料不足是一個虛線圓圈。**狀態詞一定並列**——色弱或不懂隱喻的人只看字也懂；圖示另有文字替代。顏色跟著狀態走，但不是唯一的線索。

   沒有狀態的三種情況分開寫：**資料不足**（之後會有、現在還不夠；寫原因，規則照印）、**不適用**（這一類標的沒有這個面向；卡片不畫、總覽表寫灰字）、**暫時讀不到**（分析檔沒抓到）。
2. **不加總。** 幾個圖示並排，各自獨立；不顯示「幾個面向怎樣」之類的計數，也不做任何評分。要不要「多個面向同向才行動」是使用者自己看圖示下的判斷。
3. **每個數字下面一行白話**，說這個數字代表什麼。例：最大回檔 −52% 的下面寫「歷史上最慘的一次曾經跌掉大約一半，到現在還沒漲回原來的高點。」白話句由程式依數字套模板產生，模板寫在 `js/plain.js`、有測試；資料不足時輸出「資料不足」，不是空白。
4. **名詞點一下有解釋**：波動、回檔、百分位、相關係數、追蹤差、折溢價、即期、現鈔（以及價差、中位數、歷史模擬、預算池；A1-7 加了面向、狀態詞、保底、視窗重疊，與 RSI、移動平均、區間位置等指標的名字）——每個詞一句話的定義，全站共用同一個檔 `js/glossary.js`。點一下在旁邊展開、再點一下收起；不靠滑鼠懸停，手機也能用。
5. **換匯助手最上面是「一眼看懂」列**，三格：人民幣現在【圖示＋偏便宜／中間／偏貴】、換匯成本【圖示＋便宜／正常／偏貴，相對即期價差的一年中位數】、本月規則試算【比例＋白話一句】；下面才是位置、成本、規則表、歷史模擬的表。
6. **測試釘住的事**（把程式改壞，測試必須紅）：圖示沒有並列狀態詞；圖示沒有印規則；出現那四個詞；白話模板遇到資料不足輸出空字串；顯示任何面向計數。A1-7 再加：位置的字眼被換成「偏貴」那一套；折溢價用錯規則；不適用被寫成資料不足；總覽表顯示計數；儀表板首屏的請求超過原本的數目、首屏之後超過 5 個；A1-5 的燈號 fixture 被改壞。

## 7. 下一步

- **淨值回補**（A1-4，小停點）：用 e添富的官方每日淨值把 00646 三年的「確定」折溢價一次回補，不必等 20 個交易日累積；補完之後卡片與總覽表上 00646 的成本那一格就會從「資料不足　累積中 n/20」變成便宜／正常／偏貴（用哪一個口徑判定，到時候裁決）。
- **趨勢與情緒**（A2）、**估值與基本面**（A3）、**各面向狀態的快照紀錄與回顧**（A4）。

---

以上為量化整理，未經回測驗證，不構成投資建議。
