
在這一章,我們要實作 Backend。它的唯一職責就是:「前端跟我要什麼股票資料,我就去 Yahoo Finance 抓回來給它。」
1. 資料服務層 (Service Layer)
我們先寫一個 service.py 專門處理 yfinance 的邏輯。這樣 main.py (API 層) 就不會跟資料邏輯混在一起,這叫做 關注點分離 (Separation of Concerns)。
# backend/service.py
import yfinance as yf
import pandas as pd
def get_stock_info(ticker: str) -> dict:
"""取得股票基本資訊 (現價, 本益比...)"""
stock = yf.Ticker(ticker)
info = stock.info
return {
"symbol": info.get("symbol"),
"name": info.get("longName"),
"price": info.get("currentPrice", info.get("regularMarketPrice")),
"currency": info.get("currency"),
"pe_ratio": info.get("trailingPE"),
"eps": info.get("trailingEps")
}
def get_stock_history(ticker: str, period: str = "1mo") -> str:
"""取得歷史股價 (回傳 JSON 字串)"""
stock = yf.Ticker(ticker)
# 抓取歷史資料
df = stock.history(period=period)
# 整理資料:重設索引,讓 Date 變成欄位
df.reset_index(inplace=True)
# 轉換日期格式
df['Date'] = df['Date'].dt.strftime('%Y-%m-%d')
# 轉成 JSON 格式回傳 (使用 records 格式較易讀)
return df.to_dict(orient="records")
2. API 介面層 (Controller Layer)
接著修改 backend/main.py,建立 API 端點。
# backend/main.py
from fastapi import FastAPI, HTTPException
from service import get_stock_info, get_stock_history
app = FastAPI(title="Stock API", version="1.0.0")
@app.get("/")
def read_root():
return {"message": "Welcome to Stock API"}
@app.get("/stock/{ticker}")
def read_stock_info(ticker: str):
"""取得單檔股票即時資訊"""
try:
data = get_stock_info(ticker)
if not data["price"]: # 簡單檢查
raise HTTPException(status_code=404, detail="Stock not found")
return data
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/history/{ticker}")
def read_stock_history(ticker: str, period: str = "1mo"):
"""
取得歷史股價 K 線資料
period: 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max
"""
try:
data = get_stock_history(ticker, period)
return data
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
3. 測試 API
打開終端機,啟動服務:
cd backend
uvicorn main:app --reload
用瀏覽器或 Postman 測試:
- 基本面:
http://localhost:8000/stock/AAPL-> 應回傳 Apple 的股價資訊。 - 台股測試:
http://localhost:8000/stock/2330.TW-> 應回傳台積電資訊。 - K 線資料:
http://localhost:8000/history/NVDA?period=1y-> 應回傳 NVIDIA 一年走勢。
4. 總結
我們成功建立了一個微服務 (Microservice)! 它不關心誰來呼叫它 (可能是網頁、可能是手機 App),它只負責吐出標準的 JSON 資料。
下一章,我們將切換到 Frontend,使用 Streamlit 來把這些冰冷的 JSON 數據變成漂亮的互動儀表板!
除錯小撇步:
如果 yfinance 報錯,通常是因為網路連線問題或股票代碼錯誤。可以嘗試更新套件:pip install --upgrade yfinance。