※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
Zスコアとは何か
Zスコア(Z-score)とは、ある値が平均からどれだけ離れているかを標準偏差の倍数で表した指標です。「標準化」とも呼ばれます。
計算式はシンプルです:Z = (観測値 – 平均) ÷ 標準偏差
| Zスコアの値 | 意味 | 株価への適用 |
|---|---|---|
| +2以上 | 平均より2σ以上高い(上位2.3%) | 統計的に割高・買われすぎの可能性 |
| +1〜+2 | やや高め | 上昇トレンドの継続 |
| -1〜+1 | 通常の範囲 | 中立 |
| -2以下 | 平均より2σ以上低い(下位2.3%) | 統計的に割安・売られすぎの可能性 |
ペア取引(スタット・アービトラージ)への応用
Zスコアはペア取引でもよく使われます。2つの相関が高い銘柄の価格比(スプレッド)にZスコアを適用し、「Zスコアが+2以上になったら割高側を売り・割安側を買う」という手法です。
Pythonで割高・割安シグナルを検出する
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.family'] = 'Meiryo'
# ==============================
# 設定エリア
# ==============================
SYMBOL = "7203.T" # 分析対象銘柄
PERIOD = "1y" # 期間
WINDOW = 20 # Zスコア計算のローリングウィンドウ(日数)
Z_UPPER = 2.0 # 買われすぎのZスコア閾値
Z_LOWER = -2.0 # 売られすぎのZスコア閾値
# ==============================
# データ取得とZスコア計算
# ==============================
df = yf.Ticker(SYMBOL).history(period=PERIOD)
close = df["Close"]
# ローリングZスコア(直近WINDOW日間の平均・標準偏差を使って計算)
rolling_mean = close.rolling(window=WINDOW).mean()
rolling_std = close.rolling(window=WINDOW).std()
z_score = (close - rolling_mean) / rolling_std
# シグナル判定
signal = pd.Series("中立", index=close.index)
signal[z_score >= Z_UPPER] = "買われすぎ(売り候補)"
signal[z_score <= Z_LOWER] = "売られすぎ(買い候補)"
# 直近の状況を出力
print(f"直近{WINDOW}日間のZスコア分析 [{SYMBOL}]")
print(f"現在のZスコア: {z_score.iloc[-1]:.2f}")
print(f"判定: {signal.iloc[-1]}")
print()
# Zスコアが2以上または-2以下の日数
count_high = (z_score >= Z_UPPER).sum()
count_low = (z_score <= Z_LOWER).sum()
print(f"買われすぎ(Z≥{Z_UPPER})の日数: {count_high}日")
print(f"売られすぎ(Z≤{Z_LOWER})の日数: {count_low}日")
# ==============================
# グラフ描画
# ==============================
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# 上段:株価チャート
ax1.plot(close.index, close, color='steelblue', linewidth=1)
# 買われすぎ・売られすぎのポイントをマーク
ax1.scatter(close[z_score >= Z_UPPER].index,
close[z_score >= Z_UPPER], color='red', s=40, zorder=5, label=f'買われすぎ(Z≥{Z_UPPER})')
ax1.scatter(close[z_score <= Z_LOWER].index,
close[z_score <= Z_LOWER], color='green', s=40, zorder=5, label=f'売られすぎ(Z≤{Z_LOWER})')
ax1.set_title(f"{SYMBOL} 株価とZスコアシグナル")
ax1.legend(fontsize=9)
ax1.grid(True, alpha=0.3)
# 下段:Zスコア
ax2.plot(z_score.index, z_score, color='purple', linewidth=1, label='Zスコア')
ax2.axhline(y=Z_UPPER, color='red', linestyle='--', alpha=0.7, label=f'上限 Z={Z_UPPER}')
ax2.axhline(y=Z_LOWER, color='green', linestyle='--', alpha=0.7, label=f'下限 Z={Z_LOWER}')
ax2.axhline(y=0, color='gray', linestyle='-', alpha=0.4)
ax2.set_title("Zスコア(ローリング20日)")
ax2.legend(fontsize=9)
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("z_score_signal.png", dpi=150)
plt.show()
print("グラフを保存しました: z_score_signal.png")
まとめ
- Zスコア = (観測値 - 平均)÷ 標準偏差 で正規化された位置を表す
- ±2以上が「統計的に異常な水準」の目安(正規分布なら95%の外側)
- ローリングウィンドウを使えば「最近の相場感」に基づく評価ができる
- 平均回帰戦略の入口として有効なシグナル

