yfinanceで株価バックテストを始める方法【Python・コード付き】


「投資戦略をPythonで検証してみたい」——その第一歩が yfinance です。

無料で株価データを取得でき、数行で移動平均やリターンを計算できます。この記事では、yfinanceで株価を取得し、移動平均クロス戦略を実際にバックテストするところまでを、動くコード付きで解説します。

そして最後に、初心者が必ずハマる3つの落とし穴——先読みバイアス・生存者バイアス・取引コスト無視——を共有します。ここを知らないと「バックテストでは儲かったのに実運用で負ける」戦略を量産してしまいます。

⚠️ 本記事は教育目的の情報共有です。投資は自己責任で行ってください。

目次

  1. yfinanceとは
  2. インストールと株価取得
  3. 移動平均の計算
  4. 移動平均クロス戦略のバックテスト
  5. パフォーマンス指標(リターン・最大ドローダウン)
  6. 初心者がハマる3つの落とし穴
  7. まとめ

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) までどうぞ。

⚠️ 免責:本記事は教育目的の情報共有であり、投資助言ではありません。実際の投資判断はご自身の責任で行ってください。