J-Quants APIで日本製造株のバックテスト環境をPythonで整えた話

Python実装・コード

年利25%のバックテストが、実運用2週間で吹き飛んだ話

少し前に、自作のシンプルなブレイクアウト戦略を日本製鉄でバックテストしたら年利25%という嘘みたいな数字が出て。。。意気揚々と運用したら、最初の2週間で含み損-8%。子供の世話してたら気づいたらやられてました笑。なぜそうなったのか調べているうちに、「そもそも日本株のバックテスト環境がガバガバだった」と気づいたので、今回は環境を整え直すところから記事にします。

なぜyfinanceから乗り換えたかったのか

僕は普段、トヨタや日立、信越化学みたいな日本の製造メーカー株を中心に見ています。yfinanceでもデータは取れるんですが、調整後株価のズレや、決算日・分割の反映タイミングが微妙にズレることがあって、バックテストの数字を信用しきれないんですよね。実運用との乖離の半分くらいは、ここに原因があった気がしています。

J-Quants APIを選んだ理由

J-Quants APIは東証グループ(JPX)が公式に提供している日本株データAPIで、株価・財務・銘柄属性をきれいな形で取れます。無料のFreeプランでも過去2年分の日次データに触れるので、まずは試すにはちょうどいいです。配当や分割の調整が公式基準で入ってくるので、yfinanceでありがちな「過去の急騰が分割未調整で発生したノイズだった」みたいな事故が起こりにくいのが個人的にはありがたいポイントです。

環境構築(最低限)

まずは公式サイトでメール登録してリフレッシュトークンを取得します。あとは requestspandas があればOK。僕は backtesting.py でバックテスト回したいので、それも入れておきます。

pip install requests pandas backtesting

ID/PWからIDトークンを取得して株価を引く

最初の一歩として、日本製鉄(5401)の日足を取ってきて、20日移動平均で簡単なクロス戦略を回してみます。

import os
import requests
import pandas as pd

JQ_MAIL = os.environ["JQ_MAIL"]
JQ_PASS = os.environ["JQ_PASS"]

# 1) refresh token
r = requests.post(
    "https://api.jquants.com/v1/token/auth_user",
    json={"mailaddress": JQ_MAIL, "password": JQ_PASS},
)
refresh = r.json()["refreshToken"]

# 2) id token
r = requests.post(
    f"https://api.jquants.com/v1/token/auth_refresh?refreshtoken={refresh}"
)
id_token = r.json()["idToken"]
headers = {"Authorization": f"Bearer {id_token}"}

# 3) 日本製鉄(5401) の日足
r = requests.get(
    "https://api.jquants.com/v1/prices/daily_quotes",
    params={"code": "5401", "from": "2024-01-01", "to": "2025-12-31"},
    headers=headers,
)
df = pd.DataFrame(r.json()["daily_quotes"])
df["Date"] = pd.to_datetime(df["Date"])
df = df.set_index("Date")[["Open", "High", "Low", "Close", "Volume"]]
df = df.rename(columns=str.title).dropna()
print(df.tail())

20日/75日のゴールデンクロス戦略を試す

ありきたりな例ですが、まずは古典的な移動平均クロスをbacktesting.pyで回します。手数料も現実に近い値(0.1%)で入れておくのが大事です。これを忘れて爆益だと喜ぶの、本当によくやります。。。

from backtesting import Backtest, Strategy
from backtesting.lib import crossover
import pandas as pd

class SmaCross(Strategy):
    fast, slow = 20, 75
    def init(self):
        close = pd.Series(self.data.Close)
        self.sma_fast = self.I(lambda x: pd.Series(x).rolling(self.fast).mean(), close)
        self.sma_slow = self.I(lambda x: pd.Series(x).rolling(self.slow).mean(), close)
    def next(self):
        if crossover(self.sma_fast, self.sma_slow):
            self.buy()
        elif crossover(self.sma_slow, self.sma_fast):
            self.position.close()

bt = Backtest(df, SmaCross, cash=1_000_000, commission=0.001)
print(bt.run())

まとめ:きれいなデータは「攻め」より先に整えるべき

結局のところ、戦略を磨くより先に「信頼できるデータでバックテストできる土台」を整えるほうが、僕みたいな素人にはずっと効くと感じました。次は同じ環境で、トヨタ・日立・信越化学あたりを並べて、製造メーカーセクターでまとめてバックテストできるスクリプトを書こうと思っています。同じように「yfinanceで満足してたけど数字が信用できない」と感じている方は、一度J-Quantsを触ってみるといいかもしれません。

タイトルとURLをコピーしました