iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
AI & Data

雲端情人 - AI 愛系列 第 17

Day 17 HER會幫我理財看股市小助理: 增加輸入股號的的相容相性—台股 ETF 代碼正規化、金價雙來源回退、新聞 API 防呆

  • 分享至 

  • xImage
  •  

為了像電影主角一樣利用AI分析資料提供男主角我有辦法財富自由,未來可以打照出有實體的 ai情人
把有關金融分析工具不斷做修正和一下大升級

**LINE 股市 AI 助理:錯誤韌性大升級—台股 ETF 代碼正規化、金價雙來源回退、新聞 API 防呆
**
今天把「查不到、回不來、整段爆掉」的三大痛點一次清掉:
1. 台股 ETF/特別股代碼(像 00937B、00929、2881A)全面自動補 .TW;
2. 金價用台銀頁面為主、期貨+匯率為備援;
3. 新聞 API 全面韌性化,就算掛了也不影響整體回覆。外加翻譯模式行為修復。

本日目標
• 使用者丟「00937b」「00929」「0050」都能正確查到(不再出現 possibly delisted)。
• 「金價」不再顯示「暫無法取得」,改為主來源 + 備援。
• 新聞來源失敗不會拖垮整份股票報告。
• 翻譯模式(翻譯->英文 / 翻譯->結束)行為直覺、穩定。

  1. 代碼正規化:ETF/特別股一網打盡

台股代碼除了純數字,還常見尾碼字母(ETF、特別股、權證),例如 00937B。
做法很簡單:凡是「4–6 碼數字 + 可選 1 個英文字母」,一律補上 .TW。

def _normalize_ticker_input(user_text: str) -> str:
    s_up = user_text.strip().upper()
    # 2330 / 0050 / 00937B / 2881A → *.TW
    if re.fullmatch(r"\d{4,6}[A-Z]?", s_up):
        return f"{s_up}.TW"
    return s_up  # NVDA / AAPL / ^GSPC 等維持原樣

✅ 這段放在 app_fastapi.py 的股票入口判斷前,就能讓 YahooStock、yfinance 一次吃到正確代碼。

  1. YahooStock 快照補強:Header + Retry

Yahoo 的 quote API 有時會挑 header 或短暫 40x/50x。
在 my_commands/stock/YahooStock.py 我們做了兩件事:
標準瀏覽器 UA(降低被擋風險)
• 簡單 Retry(3 次、遞增等待)

DEFAULT_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    "Accept": "application/json",
}

def _retry(times=3, delay=1.2):
    def deco(fn):
        def wrap(*a, **kw):
            last = None
            for i in range(times):
                try:
                    return fn(*a, **kw)
                except Exception as e:
                    last = e
                    time.sleep(delay * (i + 1))
            raise last
        return wrap
    return deco

✅ 初始化失敗不拋例外、只把欄位設為 None,確保分析流程不中斷。

  1. 金價雙來源:台銀為主、期貨+匯率為備援

主來源:台灣銀行黃金牌價(賣出價,TWD/克)

台銀頁面表格偶爾調整,我們不鎖死欄位順序,而是掃表、找含「賣出」欄位與「黃金/1公克/牌價」關鍵字的列。

def _parse_tbb_html_to_price_twd_per_gram(html: str) -> Optional[float]:
    soup = BeautifulSoup(html, "html.parser")
    for table in soup.find_all("table"):
        # 找 header 取得「賣出」欄位索引
        ...
        # 掃行,找「黃金/1公克/牌價」等關鍵字
        ...
        return float(value)  # TWD/克
    return None

備援:COMEX 金期貨 + USD/TWD 匯率 → 換算 TWD/克
	•	GC=F(美元/盎司)
	•	1 troy oz = 31.1034768 g
	•	乘上即時 USD→TWD 匯率

def xau_twd_per_gram_fallback() -> Optional[float]:
    px = yf.Ticker("GC=F").fast_info.get("last_price") or ...
    usdtwd = _fetch_usdtwd_rate()  # open.er-api.com
    return round(px * usdtwd / 31.1034768, 2) if px and usdtwd else None

✅ 先爬台銀,失敗再退 GC=F + 匯率。兩個都失敗才回覆「暫時無法取得」。

  1. 新聞 API 韌性:永不拖垮主流程

cnyes 會偶爾擋或變更回應,requests.get(...).json() 直接丟會導致 JSONDecodeError。
做法:先 text,再手動 json.loads;若失敗就回空字串,不拋例外。

def stock_news(keyword: str, limit: int = 5) -> str:
    try:
        url = f"https://ess.api.cnyes.com/ess/api/v1/news/keyword?q={keyword}&limit={limit}&page=1"
        r = requests.get(url, timeout=6, headers={"User-Agent": UA})
        if r.status_code != 200 or not r.text.strip().startswith("{"):
            return ""
        data = json.loads(r.text)
        ...
        return "\n".join(lines)
    except Exception:
        return ""   # 失敗就安靜地給空字串

✅ 主流程永遠能產生分析報告;沒有新聞就寫「(新聞來源暫時無法取得)」。

  1. 翻譯模式修復:狀態清楚、行為一致
    • 翻譯->英文 / 翻譯->日文 / 翻譯->繁體中文:開啟、記住目標語。
    • 翻譯->結束:清除狀態。
    • 只在一般對話分支最後套用翻譯,避免覆寫其他指令。
if low.startswith("翻譯->"):
    lang = msg.split("->", 1)[1].strip()
    if lang == "結束":
        translation_states.pop(chat_id, None)
        return reply("✅ 已結束翻譯模式")
    translation_states[chat_id] = lang
    return reply(f"🌐 已開啟翻譯 → {lang},請直接輸入要翻的內容。")
...
if chat_id in translation_states:
    out = await translate_text(msg, translation_states[chat_id])
    return reply(f"🌐 ({translation_states[chat_id]})\n{out}")

  1. 驗收清單(你可以直接照這張表測)

功能 測試指令 預期
ETF 代碼自動補 .TW 00937b、00929、0050 正常產生報告,不再出現 delisted/no timezone
台股/美股指數 台股大盤、美股大盤 產出大盤分析
金價 金價 / 黃金 有價格;主來源台銀,失敗時顯示備援來源
匯率 JPY 顯示 1 JPY ≈ TWD 的即時估值
新聞失敗容錯 暫時關網路 / 擋 cnyes 報告仍能產出,新聞段落顯示替代文字
翻譯 翻譯->英文 → 任意句子 → 翻譯->結束 翻譯模式進出清楚

  1. 今日關鍵差異(相較前一版)
    • app_fastapi.py 新增:_normalize_ticker_input()、台銀金價解析、xau_twd_per_gram_fallback()、翻譯模式順序修復。
    • YahooStock.py:加入 UA、Retry、初始化失敗不拋例外。
    • stock_news.py:改為韌性取用,永不讓 JSONDecodeError 影響主流程。

  1. 已知限制 & 後續計畫
    • 台銀頁面若大改版,主解析仍可能失敗,但備援鏈可支撐基本體驗。
    • 未來 API 建議補上多來源匯總(例如 Yahoo、Anue 兜底),再做去重,避免單API資料來源導致資料,導致分析失敗
    • 下一步:加上快取與節流(避免在群組裡被刷爆)、觀測性(Prometheus 指標 + 異常告警)。

今日成果範例(摘錄)
• 查 00937b:成功回覆 00937B.TW 的即時資訊與分析。
• 金價:即使台銀表格異動,依舊能回覆「每克 XXX 元(備援來源)」的快訊。
• 翻譯->英文:一句話即回,翻譯->結束 乾淨退場。


上一篇
Day 16|讓 LINE 金融助理系統優化:更抗風險:重試、退避、快取、斷路器與可觀測性——包含設計思路、精簡可用的程式片段與參考資料
下一篇
DAY 18|讓 her「聽你說」:語音訊息 → 轉文字 → AI 回覆(FastAPI + LINE SDK + Whisper)
系列文
雲端情人 - AI 愛21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言