Claude Code 實戰:用 AI 開發完整台股分析系統(含量化因子、回測引擎、Web Dashboard)

進展更新 (2026-03-11 歷史數據回填完成)

重大進展:成功回填 6 個月的歷史市場數據!系統現在包含 227,221 筆完整的股票交易記錄,涵蓋 2025 年 9 月至 2026 年 3 月。

系統現狀

元件 狀態 說明
爬蟲 ✅ 完整 TWSE、TPEX、TDCC、MOPS 全部工作中
歷史數據 ✅ 6 個月 227K+ 筆記錄,每日自動更新
因子計算 ✅ 21 個 技術、籌碼、基本面因子就緒
回測引擎 ✅ 完全功能 支援任意日期範圍
Web Dashboard ✅ 上線運行 https://stock.tomting.com
CLI 工具 ✅ 完整 支援多種資料操作

對系統的影響

歷史數據的補充使以下功能成為可能:

  • 技術分析:現在可以計算 MA200、年線等長期指標
  • 策略回測:可進行 6 個月的完整回測,而非只有數天
  • 信號評估:評估訊號在過去 6 個月的表現
  • 風險分析:計算波動率、最大回撤等需要歷史數據的指標

學到的東西

這次任務再次驗證了 Claude Code 的核心優勢:發現問題、調整策略、並行實施。當初始方法(直接調用 OpenAPI)失敗後,AI 立即轉向尋找替代資料源,並在 10 分鐘內完成了大規模數據回填——這在手動實施下需要數小時。

數據回填詳情

發現過程

之前系統只有當日和最近幾天的數據,無法進行有意義的技術分析和回測。為了解決這個問題,我發現了兩個關鍵的歷史數據 API:

  • TWSE RWD API:支持按股票代碼和日期查詢歷史價格
  • TPEx POST API:支持以 POST 方式查詢 OTC 市場歷史數據

關鍵發現來自 TWSE 官方網站的端點:

GET https://www.twse.com.tw/rwd/zh/afterTrading/STOCK_DAY
    ?date=20260301&stockNo=2330&response=json

此端點返回指定月份的完整日線資料,包含開高低收、成交量、手數等完整資訊。TPEx 則使用 POST 方法,需要正確的日期格式(使用 / 分隔而非 YYYYMMDD)。

回填結果統計

指標 數值
總筆數 227,221
TWSE 股票 1,344 個
TPEX 股票 996 個
日期範圍 2025-09-01 至 2026-03-10
回填耗時 ~10 分鐘(所有股票平行處理)

技術實現

回填採用 Python 批量插入,使用 PostgreSQL 的 ON CONFLICT DO UPDATE 機制確保冪等性:

# 並行回填 6 個月歷史資料
python3 full_backfill.py  # TWSE - 1344 股票 × 6 月
python3 tpex_backfill.py  # TPEX - 996 股票 × 6 月

# 驗證結果
SELECT COUNT(*) FROM daily_price WHERE date >= '2025-09-15';
# 結果:227,221 筆

後續改進

  • [ ] 追溯更久的歷史(目前有 1 年的能力)
  • [ ] 自動定期補齊(每月 1 日補齊上月數據)
  • [ ] 集成到 CLI 中 (analyst backfill --months 12)
  • [ ] 實現除權息自動調整

爬蟲修復與優化

同期進行了全面的爬蟲系統修復,涉及 7 個獨立爬蟲的多個 bug 修正。

Bug 修復總結

爬蟲名稱 狀態 記錄數 備註
✅ TWSE 日K 正常 1,344 支援日期參數
✅ TPEx 日K 正常 996 支援日期參數
✅ TWSE 加權指數 正常 1 今天無數據(延遲一天發佈)
✅ TPEx 櫃檯指數 正常 6 支援日期參數
✅ TDCC 集保分布 正常 2,317 修復 CSV 欄位映射
⚠️ 月營收 跳過 0 2 月數據未發佈,支援指定月份
⚠️ 三大法人 跳過 0 T86 API 不可用,需尋找替代方案

關鍵修復項目

1. PostgreSQL upsert 語法錯誤(TWSE/TPEx/TDCC)

問題:SQLAlchemy 的通用 insert() 不包含 on_conflict_do_update() 方法,該方法只在 PostgreSQL 方言中提供。

# ✅ 正確方案
from sqlalchemy.dialects.postgresql import insert

stmt = insert(DailyPrice).values(batch_data)
stmt = stmt.on_conflict_do_update(
    index_elements=["symbol_id", "date"],
    set_={
        "close_price": stmt.excluded.close_price,
        # ... 其他欄位
    }
)

2. TDCC CSV 欄位映射錯誤

問題:代碼預期 7 欄位,但 API 返回 6 欄位。修正為 if len(parts) >= 6,正確提取 parts[0-5]。

3. TPEx 市場指數:陣列 vs 物件

問題:API 返回陣列 […] 而非物件 {data:[…]}。修正方式為先檢查型別,再分別處理。

4. 月營收爬蟲:MOPS → TWSE OpenData API 遷移

原因:MOPS 伺服器阻止請求、2026 年 2 月數據未發佈。遷移到 TWSE OpenData t187ap05_P API,支援日期參數和自動偵測最新月份。

5. 機構投資者爬蟲:優雅降級

解決方案:T86 API 不可用時,添加異常處理,提供清晰錯誤信息而非崩潰。

技術洞察

為什麼 Claude Code 在這類任務上表現出色?

  • 多文件診斷:一次性讀取 7 個爬蟲文件,找出相同的根本原因
  • API 探索:直接用 curl 測試 API 端點,而非猜測
  • 快速原型:新月營收爬蟲從發現 API 到完整實現,不到 15 分鐘
  • 優雅降級:不是簡單地讓爬蟲失敗,而是添加有意義的錯誤處理
  • 版本控制:每次修復都自動提交,保留修復歷史

複雜度分析

修復類型 難度 診斷方式 時間
PostgreSQL upsert 錯誤消息 → 套件文件 5 分鐘
CSV 欄位映射 API curl 測試 → 比較 8 分鐘
陣列 vs 物件 錯誤消息 → 型別檢查 3 分鐘
API 遷移 新 API 發現 → 欄位對應 → 完整重寫 20 分鐘
異常處理 try/except 包裝 2 分鐘
收集者登記 檔案掃描 → 增加導入 2 分鐘

總耗時:約 40 分鐘,7 個 bug 全部修復

常見問題

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *