※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
「ドル円が上がると日経平均も上がる」という話を聞いたことがあるはずです。実際に日本株を取引していると、為替の動きが株価に大きく影響する場面を何度も目にします。
この「為替と株価の連動性」を数値で把握できれば、投資判断の精度は格段に上がります。感覚ではなくデータに基づいた分析が可能になるからです。
しかし、多くの個人投資家は「なんとなく連動している気がする」という曖昧な認識のまま取引しています。相関の強さや、時期による変化を定量的に確認していないケースがほとんどです。
原因はシンプルで、相関分析の手法やツールを知らないことにあります。Excelでも計算は可能ですが、データ取得から可視化まで一気通貫で行うにはPythonが圧倒的に効率的です。
本記事では、yfinanceを使ってドル円と日経平均のデータを取得し、相関係数(Correlation Coefficient)を算出するPythonコードを提供します。さらに、ローリング相関で時期ごとの変化を可視化する応用コードも解説します。
コードはすべてコピペで動作します。SBI証券などで日本株を取引する前の分析ツールとして、自分の投資ワークフローに組み込んでください。
相関係数の基本とドル円・日経平均の関係
相関係数とは何を示す数値か
相関係数(Correlation Coefficient)は、2つのデータ系列の連動性を-1から+1の範囲で示す統計指標です。+1に近いほど「一方が上がればもう一方も上がる」正の相関が強いことを意味します。
-1に近ければ逆方向に動く負の相関、0に近ければ連動性がないことを示します。一般的に、絶対値が0.7以上なら「強い相関」、0.4〜0.7なら「中程度の相関」と判断してください。
ドル円と日経平均が連動する構造的理由
日経平均構成銘柄の多くは輸出企業です。円安(ドル円上昇)になると海外売上の円換算額が増え、企業業績の押し上げ要因になります。
このため、ドル円と日経平均は正の相関を示す傾向があります。ただし、相関の強さは時期によって大きく変動します。金融政策の転換期やリスクオフ局面では、相関が崩れることも珍しくありません。
【コピペOK】ドル円×日経平均の相関係数を算出するメインコード
まず、必要なライブラリをインストールしてください。
pip install yfinance pandas matplotlib japanize-matplotlib
以下がメインコードです。
import datetime
from typing import Optional
import pandas as pd
import yfinance as yf
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import japanize_matplotlib # noqa: F401 – 日本語フォント有効化
# ==============================
# 設定エリア
# ==============================
TICKER_FX: str = "JPY=X" # ドル円ティッカー(Yahoo Finance形式)
TICKER_INDEX: str = "^N225" # 日経平均ティッカー
START_DATE: str = "2020-01-01" # データ取得開始日
END_DATE: str = "2025-12-31" # データ取得終了日
ROLLING_WINDOW: int = 60 # ローリング相関の窓幅(営業日)
OUTPUT_SCATTER: str = "scatter_usdjpy_nikkei.png"
OUTPUT_ROLLING: str = "rolling_correlation.png"
# ==============================
# データ取得
# ==============================
def fetch_close_data(
ticker: str,
start: str,
end: str,
) -> pd.Series:
'データを取得し終値Seriesを返す'
df: pd.DataFrame = yf.download(
ticker,
start=start,
end=end,
auto_adjust=True,
progress=False,
)
if df.empty:
raise ValueError(f"{ticker} のデータ取得に失敗しました。ティッカーを確認してください。")
close: pd.Series = df["Close"].squeeze()
close.name = ticker
return close
# ==============================
# 日次リターン算出
# ==============================
def calc_daily_returns(series: pd.Series) -> pd.Series:
'終値から日次変化率を算出する'
returns: pd.Series = series.pct_change().dropna()
return returns
# ==============================
# 相関係数算出
# ==============================
def calc_correlation(
s1: pd.Series,
s2: pd.Series,
) -> float:
'2系列のピアソン相関係数を返す'
merged: pd.DataFrame = pd.concat([s1, s2], axis=1).dropna()
corr_value: float = merged.iloc[:, 0].corr(merged.iloc[:, 1])
return round(corr_value, 4)
# ==============================
# ローリング相関算出
# ==============================
def calc_rolling_correlation(
s1: pd.Series,
s2: pd.Series,
window: int,
) -> pd.Series:
'ローリング相関のSeriesを返す'
merged: pd.DataFrame = pd.concat([s1, s2], axis=1).dropna()
rolling_corr: pd.Series = merged.iloc[:, 0].rolling(window).corr(merged.iloc[:, 1])
rolling_corr.name = f"rolling_{window}d_corr"
return rolling_corr.dropna()
# ==============================
# 可視化:散布図
# ==============================
def plot_scatter(
ret_fx: pd.Series,
ret_idx: pd.Series,
corr_value: float,
filepath: str,
) -> None:
'日次リターンの散布図を保存する'
merged: pd.DataFrame = pd.concat([ret_fx, ret_idx], axis=1).dropna()
fig, ax = plt.subplots(figsize=(8, 6))
ax.scatter(merged.iloc[:, 0], merged.iloc[:, 1], alpha=0.4, s=10)
ax.set_xlabel("ドル円 日次リターン")
ax.set_ylabel("日経平均 日次リターン")
ax.set_title(f"ドル円 vs 日経平均(相関係数: {corr_value})")
ax.axhline(0, color="gray", linewidth=0.5)
ax.axvline(0, color="gray", linewidth=0.5)
fig.tight_layout()
fig.savefig(filepath, dpi=150)
plt.close(fig)
print(f"散布図を保存しました: {filepath}")
# ==============================
# 可視化:ローリング相関
# ==============================
def plot_rolling_correlation(
rolling_corr: pd.Series,
window: int,
filepath: str,
) -> None:
'ローリング相関の時系列チャートを保存する'
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(rolling_corr.index, rolling_corr.values, linewidth=1.0)
ax.set_ylabel("相関係数")
ax.set_title(f"ドル円×日経平均 {window}日ローリング相関")
ax.axhline(0, color="red", linestyle="--", linewidth=0.8)
ax.axhline(0.7, color="green", linestyle="--", linewidth=0.6, label="±0.7ライン")
ax.axhline(-0.7, color="green", linestyle="--", linewidth=0.6)
ax.legend()
fig.tight_layout()
fig.savefig(filepath, dpi=150)
plt.close(fig)
print(f"ローリング相関チャートを保存しました: {filepath}")
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
# 1. データ取得
close_fx: pd.Series = fetch_close_data(TICKER_FX, START_DATE, END_DATE)
close_idx: pd.Series = fetch_close_data(TICKER_INDEX, START_DATE, END_DATE)
# 2. 日次リターン算出
ret_fx: pd.Series = calc_daily_returns(close_fx)
ret_idx: pd.Series = calc_daily_returns(close_idx)
# 3. 全期間の相関係数
corr_all: float = calc_correlation(ret_fx, ret_idx)
print(f"全期間の相関係数: {corr_all}")
# 4. ローリング相関
rolling_corr: pd.Series = calc_rolling_correlation(
ret_fx, ret_idx, ROLLING_WINDOW,
)
# 5. 可視化
plot_scatter(ret_fx, ret_idx, corr_all, OUTPUT_SCATTER)
plot_rolling_correlation(rolling_corr, ROLLING_WINDOW, OUTPUT_ROLLING)
# 6. 直近の相関係数を表示
recent_corr: float = round(rolling_corr.iloc[-1], 4)
print(f"直近{ROLLING_WINDOW}日の相関係数: {recent_corr}")
コードの処理フロー解説
上記のコードは、以下の6ステップで構成されています。
* ステップ1 データ取得:yf.download()でドル円(JPY=X)と日経平均(^N225)の終値を取得する
* ステップ2 日次リターン算出:pct_change()で前日比の変化率を計算する。終値そのものではなく変化率で比較することが統計的に正しい手法である
* ステップ3 全期間相関係数:pandasのcorr()メソッドでピアソン相関係数を算出する
* ステップ4 ローリング相関:直近60営業日(約3か月)の窓幅で相関係数を日々算出し、時期ごとの変化を捉える
* ステップ5 可視化:散布図とローリング相関チャートをPNG画像として保存する
* ステップ6 直近値表示:ローリング相関の最新値をコンソールに出力する
ROLLING_WINDOWの値を20(約1か月)や120(約半年)に変更すれば、分析の時間軸を柔軟に切り替えられます。
出力結果の読み解き方と投資判断への活かし方
散布図から読み取るべきポイント
散布図で右上がりの分布になっていれば、正の相関を視覚的に確認できます。点のばらつきが小さいほど相関が強く、大きく散らばっていれば相関が弱いと判断してください。
特に注目すべきは外れ値(Outlier)です。散布図の端にある異常値は、ショック相場やイベント時のデータであることが多く、平時の相関とは分けて考える必要があります。
ローリング相関の変化が示すもの
ローリング相関チャートで0.7を超えている期間は、為替と株価が強く連動していた時期です。この期間ではドル円の動きを見て日経平均のポジションを調整する戦略が有効に機能します。
逆に相関が0付近やマイナスに転じた期間は、為替以外の要因が株価を動かしていたことを意味します。相関の低下局面では、為替だけを頼りにした売買判断は危険です。
SBI証券での実践的な使い方
SBI証券で日本株を取引する際、朝の寄り付き前にこのコードを実行してください。直近60日の相関係数が0.6以上であれば、前夜のNY市場でのドル円の動きが日経平均の方向性を強く示唆します。
相関が0.3以下に低下している場合は、為替要因の影響が弱まっているサインです。業績やセクター固有の材料に注目を切り替えてください。
【コピペOK】年別・期間別に相関を比較する応用コード
import pandas as pd
import yfinance as yf
# ==============================
# 設定エリア
# ==============================
TICKER_FX: str = "JPY=X"
TICKER_INDEX: str = "^N225"
START_DATE: str = "2015-01-01"
END_DATE: str = "2025-12-31"
# ==============================
# データ取得・リターン算出(メインコードと共通)
# ==============================
def fetch_and_calc_returns(ticker: str, start: str, end: str) -> pd.Series:
'終値取得→日次リターンを返す'
df: pd.DataFrame = yf.download(
ticker, start=start, end=end, auto_adjust=True, progress=False,
)
close: pd.Series = df["Close"].squeeze()
return close.pct_change().dropna()
# ==============================
# 年別相関テーブル生成
# ==============================
def build_yearly_corr_table(
ret_fx: pd.Series,
ret_idx: pd.Series,
) -> pd.DataFrame:
'年ごとの相関係数をDataFrameで返す'
merged: pd.DataFrame = pd.concat([ret_fx, ret_idx], axis=1).dropna()
merged.columns = ["FX", "INDEX"]
merged["year"] = merged.index.year
records: list[dict] = []
for year, group in merged.groupby("year"):
corr_val: float = round(group["FX"].corr(group["INDEX"]), 4)
records.append({"年": year, "相関係数": corr_val, "営業日数": len(group)})
return pd.DataFrame(records)
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
ret_fx: pd.Series = fetch_and_calc_returns(TICKER_FX, START_DATE, END_DATE)
ret_idx: pd.Series = fetch_and_calc_returns(TICKER_INDEX, START_DATE, END_DATE)
table: pd.DataFrame = build_yearly_corr_table(ret_fx, ret_idx)
print(table.to_string(index=False))
コードの処理フロー解説
上記のコードは、以下の3ステップで構成されています。
* ステップ1 長期データ取得:2015年から直近までのデータを一括取得し、日次リターンを算出する
* ステップ2 年別グルーピング:groupby("year")で年単位に分割し、各年の相関係数を計算する
* ステップ3 テーブル出力:年・相関係数・営業日数をDataFrameとしてコンソールに表示する
出力されるテーブルは以下のようなイメージです。
| 年 | 相関係数 | 営業日数 |
|---|---|---|
| 2020 | 0.45 | 245 |
| 2021 | 0.52 | 245 |
| 2022 | 0.71 | 245 |
| 2023 | 0.63 | 245 |
| 2024 | 0.58 | 245 |
年によって相関係数が大きく異なることが確認できます。START_DATEを変更すれば、リーマンショック期やアベノミクス期まで遡った分析も可能です。
よくあるエラーと対処法
No data found for this date range
yfinanceがデータを取得できなかった場合に発生します。Yahoo FinanceのAPIは仕様変更が多く、一時的にデータが返らないことがあります。
以下を試してください。
* yfinanceを最新版に更新する(pip install --upgrade yfinance)
* START_DATEを直近1年程度に狭めて再実行する
* 数分〜数時間待ってから再度実行する(一時的なAPI制限の可能性がある)
ValueError: could not convert string to float
データ型の不整合が原因です。yfinanceのバージョンによって返却されるDataFrameの構造が変わることがあります。
以下を試してください。
* df["Close"].squeeze()で確実にSeriesに変換されているか確認する
* print(df.dtypes)でカラムの型を出力し、object型になっていないかチェックする
* df["Close"] = pd.to_numeric(df["Close"], errors="coerce")で強制的に数値型に変換する
日本語フォントが文字化けする
matplotlibはデフォルトで日本語フォントを持っていません。japanize-matplotlibのインストール漏れが最も多い原因です。
以下を試してください。
* pip install japanize-matplotlibを実行し、import japanize_matplotlibがコード内にあるか確認する
* Google Colab環境では!pip install japanize-matplotlibのあとにランタイムを再起動する
* それでも解消しない場合は、plt.rcParams["font.family"] = "IPAexGothic"を明示的に設定する
まとめ
この記事では、Pythonを使ってドル円と日経平均の相関係数を算出し、投資判断に活かす方法を解説しました。
要点を整理します。
* 相関係数は-1〜+1の範囲で2つのデータの連動性を示す。絶対値0.7以上が「強い相関」の目安である
* 終値そのものではなく日次リターン(変化率)で相関を計算することが統計的に正しい
* ローリング相関を使うと、相関の強さが時期によって変動することを視覚的に確認できる
* 年別の相関テーブルを作成すれば、過去の相場環境ごとの特徴を定量的に比較できる
* 相関が低下している局面では為替以外の要因に注目を切り替える判断が重要になる
次のステップとして、ドル円以外の通貨ペア(ユーロ円・ポンド円)や、日経平均以外の指数(TOPIX・マザーズ指数)との相関分析に応用してください。TICKER_FXとTICKER_INDEXの値を変更するだけで対応できます。
さらに、相関係数の急変をSlackやLINEに通知する仕組みを追加すれば、相場環境の変化をリアルタイムで把握できる実用的な監視ツールに発展させられます。
