はじめに
前回までで、FastAPI の基本的な API 実装や、日経225 の構成銘柄取得などを扱ってきました。 今回は、実務でもよく使う 株価データの取得 API をテーマにします。
特に、以下の要件を満たす API を作ります。
- 銘柄コード(4桁)をパラメータで受け取る
- 東証銘柄として
.Tを自動付与 - yfinance を使って株価を取得
- 過去5年分の 月末株価 を返す
- FastAPI で JSON API として公開する
Safe 設計・再現性・教材化の観点でも非常に扱いやすい構成です。
1. yfinance で株価を取得する
まずはサービス層のコードです。 銘柄コードを受け取り、過去5年の月末株価を返す関数を作ります。
services/stock_service.py
import yfinance as yf
from datetime import datetime, timedelta
def fetch_monthly_stock(code: str):
"""
4桁の銘柄コードを受け取り、過去5年分の月末株価を返す
"""
if not code.isdigit() or len(code) != 4:
raise ValueError("銘柄コードは4桁の数字で指定してください。")
symbol = f"{code}.T" # 東証銘柄として扱う
end = datetime.today()
start = end - timedelta(days=365 * 5)
ticker = yf.Ticker(symbol)
df = ticker.history(start=start, end=end)
if df.empty:
raise RuntimeError(f"{symbol} の株価データが取得できませんでした。")
# 月末データにリサンプリング(pandas 2.2+ は "ME")
monthly_df = df.resample("ME").last()
# DataFrame → dict
monthly_df.reset_index(inplace=True)
monthly_df["Date"] = monthly_df["Date"].dt.strftime("%Y-%m-%d")
return monthly_df.to_dict(orient="records")
✔ Safe 設計ポイント
- 銘柄コードのバリデーション
.Tを自動付与- pandas の仕様変更に対応(”M” → “ME”)
- DataFrame を JSON 化しやすい形に変換
- 例外は FastAPI 側で扱いやすい RuntimeError に統一
2. FastAPI のルートを作成する
api/v1/routes.py
from fastapi import APIRouter
from app.services.nikkei_service import fetch_nikkei225_stocks
from app.services.stock_service import fetch_monthly_stock
router = APIRouter()
@router.get("/hello")
def hello():
return {"message": "Hello FastAPI!"}
@router.get("/nikkei225", tags=["Nikkei"])
def get_nikkei225():
return fetch_nikkei225_stocks()
# ここを追加
@router.get("/stocks/{code}/monthly5y")
def get_monthly_stock(code: str):
return fetch_monthly_stock(code)3. 動作確認
FastAPI を起動して、以下の URL にアクセスします。ここでは7203のトヨタの銘柄コードを使います。
http://localhost:8000/api/v1/stocks/7203/monthly5y
Swagger UI による API 動作確認もできます。(http://localhost:8000/docs)

4. 今回のまとめ
今回の API は、実務でも教育現場でも非常に使いやすい構成になっています。
- 銘柄コードをパラメータ化して汎用性を確保
- yfinance で安定した株価取得
- pandas の resample(“ME”) で月末データを抽出
- FastAPI で JSON API として公開
次回は、このデータを使って 移動平均(5/25/75)を計算する APIなどを作ろうと思っています。


コメント