前回の記事では、個別銘柄の月末株価を取得する API を作りました。 今回はさらに一歩進めて、個別銘柄の月末データと、主要指数(日経平均・S&P500・ダウ平均)をまとめて取得し、同じ日付で横に結合して返す API を実装します。
株価分析や機械学習では、
- 目的変数(個別銘柄の株価)
- 説明変数(指数やテクニカル指標) を同じ日付で揃えることが非常に重要です。
今回の API は、まさにそのための「分析用データセット生成 API」です。
1. 今回のゴール
最終的に返したいデータは次のような形です。(例:トヨタ
| Date | 7203_Close | 7203_MA25 | Nikkei | SP500 | Dow |
|---|---|---|---|---|---|
| 2024-01-31 | 3150 | 3080 | 36000 | 4800 | 38000 |
| 2024-02-29 | 3200 | 3105 | 36500 | 4850 | 38500 |
つまり、 個別銘柄の月末データ(Close + MA)と、主要指数の月末データを 1 行にまとめる ということです。
2. 処理の流れ
今回の API は次の流れで動きます。
- 個別銘柄の過去5年の日次データを取得
- 25日移動平均(MA25)を計算
- 月末だけ抽出
- 日経平均・S&P500・ダウ平均も同じ処理
- Date をキーに横結合
- JSON として返す
この仕組みにより、分析に必要な「日付で揃った特徴量セット」が自動で生成されます。
3. stock_service.py(ビジネスロジック)
まずは、株価データを取得し、月末データに加工する関数です。別記事でstock_service.pyを記述している場合は書き換えます。
# app/services/stock_service.py
import yfinance as yf
from datetime import datetime, timedelta
def fetch_monthly_features(code: str, window: int = 25):
if code.isdigit() and len(code) == 4:
symbol = f"{code}.T"
elif code in ["^N225", "^DJI", "^GSPC"]:
symbol = code
else:
raise ValueError("銘柄コードは4桁の数字、または ^N225 / ^DJI / ^GSPC を指定してください。")
end = datetime.today()
start = end - timedelta(days=365 * 5)
df = yf.Ticker(symbol).history(start=start, end=end)
if df.empty:
raise RuntimeError(f"{symbol} の株価データが取得できませんでした。")
df["MA"] = df["Close"].rolling(window=window).mean()
monthly_df = df.resample("ME").last()
monthly_df = monthly_df.dropna(subset=["MA"])
monthly_df = monthly_df[["Close", "MA"]].copy()
monthly_df.reset_index(inplace=True)
monthly_df["Date"] = monthly_df["Date"].dt.strftime("%Y-%m-%d")
return monthly_df
def fetch_merged_features(target_code: str, window: int = 25):
"""
個別銘柄 + 日経平均 + S&P500 + ダウ を
Date で横結合して1行にまとめて返す
"""
# 個別銘柄
target = fetch_monthly_features(target_code, window)
target = target.rename(columns={
"Close": f"{target_code}_Close",
"MA": f"{target_code}_MA{window}"
})
# 指数3つ
nikkei = fetch_monthly_features("^N225", window).rename(columns={"Close": "Nikkei"})
sp500 = fetch_monthly_features("^GSPC", window).rename(columns={"Close": "SP500"})
dow = fetch_monthly_features("^DJI", window).rename(columns={"Close": "Dow"})
# Date で横結合
merged = target.merge(nikkei[["Date", "Nikkei"]], on="Date", how="inner")
merged = merged.merge(sp500[["Date", "SP500"]], on="Date", how="inner")
merged = merged.merge(dow[["Date", "Dow"]], on="Date", how="inner")
return merged.to_dict(orient="records")
4. routes.py(API エンドポイント)
次に、FastAPI のルーティングです。
from fastapi import APIRouter, HTTPException
from app.services.nikkei_service import fetch_nikkei225_stocks
from app.services.stock_service import fetch_merged_features
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("/monthly-features")
def monthly_features(code: str, window: int = 25):
try:
data = fetch_merged_features(code, window)
return data
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except RuntimeError as e:
raise HTTPException(status_code=404, detail=str(e))
上記コードを記述したら、以下のコードでFastAPIを起動します。
uv run uvicorn app.main:app --reload5. 実際のエンドポイント
✔ 個別銘柄(例:7203)+ 指数3つをまとめて取得
以下のURLを叩くとデータがJSON形式で返却されます。
http://localhost:8000/api/v1/monthly-features?code=7203&window=25
docsで確認すると

パラメータを入れてテストすることもできます。

6.返却される JSON(例)
[
{
"Date": "2024-01-31",
"7203_Close": 3150,
"7203_MA25": 3080,
"Nikkei": 36000,
"SP500": 4800,
"Dow": 38000
},
{
"Date": "2024-02-29",
"7203_Close": 3200,
"7203_MA25": 3105,
"Nikkei": 36500,
"SP500": 4850,
"Dow": 38500
}
]
7. まとめ
今回の記事では、次のことができるようになりました。
- 個別銘柄の月末データ(Close + MA)を取得
- 日経平均・S&P500・ダウ平均の月末データも取得
- すべてを Date で横結合して 1 行にまとめる API を実装
- 分析や機械学習にそのまま使えるデータセットを自動生成
これにより、株価分析の前処理が大幅に効率化されます。
次回は、株価を分析予測する機械学習モデルを作ろうかと思っています。


コメント