yfinanceで株価バックテストを始める方法【Python・コード付き】
「投資戦略をPythonで検証してみたい」——その第一歩が yfinance です。
無料で株価データを取得でき、数行で移動平均やリターンを計算できます。この記事では、yfinanceで株価を取得し、移動平均クロス戦略を実際にバックテストするところまでを、動くコード付きで解説します。
そして最後に、初心者が必ずハマる3つの落とし穴——先読みバイアス・生存者バイアス・取引コスト無視——を共有します。ここを知らないと「バックテストでは儲かったのに実運用で負ける」戦略を量産してしまいます。
⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。
目次
- yfinanceとは
- インストールと株価取得
- 移動平均の計算
- 移動平均クロス戦略のバックテスト
- パフォーマンス指標(リターン・最大ドローダウン)
- 初心者がハマる3つの落とし穴
- まとめ
1. yfinanceとは
yfinance は Yahoo! Finance の株価データを取得できるPythonライブラリです。
- 無料(APIキー不要)
- 米国株・ETF・指数・為替・暗号資産に対応
- 日足・分足・配当・株式分割データも取れる
- pandas の DataFrame で返るので分析が楽
個人の戦略検証には十分すぎる機能です。
2. インストールと株価取得
pip install yfinance pandas
AAPL(Apple)の過去1年の日足を取得してみます。
import yfinance as yf
# 過去1年の日足を取得
df = yf.download("AAPL", period="1y", interval="1d", progress=False)
print(df.tail())
print(f"取得行数: {len(df)}")
Open / High / Low / Close / Volume の列を持つ DataFrame が返ります。
💡 補足:yfinanceは複数銘柄を同時取得すると列が MultiIndex になり、扱いにくくなることがあります。1銘柄ずつ取得してフラット化すると安定します。
3. 移動平均の計算
pandas の rolling で移動平均は一発です。
df["SMA_short"] = df["Close"].rolling(20).mean() # 20日移動平均
df["SMA_long"] = df["Close"].rolling(50).mean() # 50日移動平均
4. 移動平均クロス戦略のバックテスト
定番の「ゴールデンクロス(短期線が長期線を上抜け)で買い、デッドクロスで売り」を検証します。
import numpy as np
# シグナル: 短期 > 長期 なら保有(1)、そうでなければ現金(0)
df["signal"] = (df["SMA_short"] > df["SMA_long"]).astype(int)
# ★重要: シグナルは「前日終値」で確定 → 翌日から建玉。
# shift(1) を忘れると「未来の情報で取引」する先読みバイアスになる
df["position"] = df["signal"].shift(1).fillna(0)
# 日次リターン
df["daily_return"] = df["Close"].pct_change()
# 戦略リターン = ポジション × 日次リターン
df["strategy_return"] = df["position"] * df["daily_return"]
# 累積リターン(複利)
df["cumulative"] = (1 + df["strategy_return"]).cumprod()
print(f"戦略の最終リターン: {(df['cumulative'].iloc[-1] - 1) * 100:+.1f}%")
buy_hold = (df['Close'].iloc[-1] / df['Close'].iloc[df['SMA_long'].notna().idxmax()] - 1) * 100
print(f"(参考)バイ&ホールド: {buy_hold:+.1f}%")
shift(1) が肝です。これが無いと「その日のクロスを見てその日の始値で買う」という現実には不可能な取引になり、成績が過大評価されます。
5. パフォーマンス指標
リターンだけでなく、どれだけ含み損に耐える必要があったか(最大ドローダウン)も必ず見ます。
# 最大ドローダウン
cum = df["cumulative"].dropna()
peak = cum.cummax()
drawdown = (cum - peak) / peak
max_dd = drawdown.min() * 100
print(f"最大ドローダウン: {max_dd:.1f}%")
# シャープレシオ(年率・リスクフリー0と仮定)
sr = df["strategy_return"].mean() / df["strategy_return"].std() * np.sqrt(252)
print(f"シャープレシオ: {sr:.2f}")
- 最大ドローダウン:ピークからの最大下落率。-30%なら「100万が70万になる場面に耐える」覚悟が要る
- シャープレシオ:リスク1単位あたりのリターン。1.0以上なら良好
6. 初心者がハマる3つの落とし穴
ここが本記事で一番伝えたいことです。私自身、全部ハマりました。
落とし穴1:先読みバイアス(Look-ahead bias)
「未来の情報」を使って取引してしまう最頻出ミス。
- ❌ 当日のクロスを見て当日の終値で買う
- ✅ 前日までの情報で判断 → 翌日に取引(
shift(1))
これを忘れると、どんな戦略も魔法のように勝ちます。勝ちすぎる結果は、まず先読みを疑う。
落とし穴2:生存者バイアス(Survivorship bias)
「現在も上場している銘柄」だけでバックテストすると、倒産・上場廃止した銘柄が除外され、成績が過大評価されます。
- 例:「現在のS&P500」で20年前から検証 → 当時はまだ無名で、その後勝ち残った銘柄だけを最初から知っている状態
- 対策:当時の構成銘柄(point-in-time データ)を使う。難しければ「結果を割り引いて見る」
落とし穴3:取引コスト・スリッページの無視
手数料が無料でも、**売買のたびにスプレッド(買値と売値の差)**で少しずつ削られます。
- 月次リバランスで年60回売買 → 往復0.1%でも年6%のドラッグ
- バックテストに
往復コストを引いて初めて現実的な数字になる
# 取引コストを引く例(ポジション変化時に往復コスト)
cost_per_trade = 0.001 # 0.1%
df["trade"] = df["position"].diff().abs()
df["strategy_return"] -= df["trade"] * cost_per_trade
7. まとめ
yfinanceを使えば、Pythonで株価バックテストが驚くほど手軽に始められます。
- ✅
yf.download()で無料データ取得 - ✅
rollingで移動平均、shift(1)で先読み回避 - ✅ リターンだけでなく最大ドローダウンを必ず見る
- ⚠️ 先読み・生存者バイアス・取引コストの3点を外すと「机上で勝つ嘘戦略」になる
「バックテストで勝った」は出発点にすぎません。**過去に最適化しただけの戦略を見破る「ウォークフォワード検証」**を必ず通してください → ウォークフォワード検証とは?過剰最適化を見破る方法
実際に米国株を自動売買するなら、前回の記事もどうぞ → Alpaca APIで米国株を自動売買する方法
質問や感想は X (@QuantNobu) までどうぞ。
⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。