背景知識: Sharpe Ratioの統計的罠
「あなたのSharpe 2.0戦略は、単なる統計ノイズかもしれません。」
1. Sharpe Ratioの推定誤差
1.1 なぜサンプルSharpeは信頼できないか
シャープレシオの定義はシンプルです:
SR = (μ - rf) / σ
Where:
μ = 平均リターン
rf = リスクフリーレート
σ = リターンの標準偏差
問題: μとσの両方が有限サンプルから推定され、両方に推定誤差があります。
1.2 Sharpe Ratioの標準誤差
Lo (2002)がシャープレシオの近似標準誤差を導出しました:
SE(SR) ≈ √[(1 + SR²/2) / N]
Where:
N = 観測数(例: 取引日数)
注: この式はIID(独立同一分布)リターンを仮定しています。 リターンが自己相関を示す場合(例: モメンタム効果、平均回帰)、標準誤差は 調整が必要です。Lo (2002)は自己相関調整済み標準誤差の式も提供しており、 これは持続的なリターンパターンを持つ戦略の場合、IID版よりも 大幅に大きくなる可能性があります。
紙上演習:
| 真のSharpe | 観測数N | 標準誤差SE | 95%信頼区間 |
|---|---|---|---|
| 1.0 | 252 (1年) | 0.077 | [0.85, 1.15] |
| 1.0 | 756 (3年) | 0.045 | [0.91, 1.09] |
| 2.0 | 252 (1年) | 0.109 | [1.79, 2.21] |
| 2.0 | 756 (3年) | 0.063 | [1.88, 2.12] |
主要な発見:
- 1年のデータはSharpe推定誤差が約±0.15(95%信頼区間)
- 真のSharpeが0でも、サンプルSharpeが0.06を超える確率は約16%(0.12を超えるのは約3%)
1.3 コード実装
import numpy as np
from scipy import stats
def sharpe_ratio(returns: np.ndarray,
rf: float = 0.0,
periods_per_year: int = 252) -> float:
"""年率シャープレシオを計算"""
excess_returns = returns - rf / periods_per_year
return np.sqrt(periods_per_year) * excess_returns.mean() / excess_returns.std()
def sharpe_standard_error(sr: float, n: int) -> float:
"""シャープレシオの標準誤差を計算"""
return np.sqrt((1 + sr**2 / 2) / n)
def sharpe_confidence_interval(sr: float, n: int,
confidence: float = 0.95) -> tuple:
"""シャープレシオの信頼区間を計算"""
se = sharpe_standard_error(sr, n)
z = stats.norm.ppf((1 + confidence) / 2)
return (sr - z * se, sr + z * se)
def sharpe_p_value(sr: float, n: int) -> float:
"""
シャープレシオが0より有意に大きいかテスト
H0: 真のSharpe = 0
"""
se = sharpe_standard_error(0, n) # H0の下での標準誤差
z = sr / se
return 1 - stats.norm.cdf(z)
使用例:
# 1年のバックテストデータがあると仮定、サンプルSharpe = 1.5
sr = 1.5
n = 252
se = sharpe_standard_error(sr, n)
ci = sharpe_confidence_interval(sr, n)
p = sharpe_p_value(sr, n)
print(f"Sample Sharpe: {sr:.2f}")
print(f"Standard Error: {se:.3f}")
print(f"95% Confidence Interval: [{ci[0]:.2f}, {ci[1]:.2f}]")
print(f"p-value: {p:.4f}")
# Output:
# Sample Sharpe: 1.50
# Standard Error: 0.081
# 95% Confidence Interval: [1.34, 1.66]
# p-value: 0.0000
2. Multiple Testing問題
2.1 戦略マイニングの呪い
シナリオ: 100戦略をテストして最も高いSharpeを持つものを選択しました。
問題:
- 各戦略は独立、全て真のSharpeは0
- 100をテストした後、期待される最大サンプルSharpe ≈ 0.19(純粋なノイズ!)
これは「戦略マイニング」の統計的罠です
2.2 数学的原理
K個の独立戦略をテストする場合、最大Sharpeの期待値:
E[max(SR₁, SR₂, ..., SRₖ)] ≈ SE × √(2 × ln(K))
Where SEは単一Sharpeの標準誤差
紙上演習:
| テストした戦略数K | SE = 0.063の時の期待最大Sharpe |
|---|---|
| 10 | 0.063 × √(2×ln(10)) = 0.135 |
| 100 | 0.063 × √(2×ln(100)) = 0.191 |
| 1,000 | 0.063 × √(2×ln(1000)) = 0.234 |
| 10,000 | 0.063 × √(2×ln(10000)) = 0.270 |
結論: テストする戦略が多いほど、「最良戦略」のサンプルSharpeは高くなります—全てがノイズでも。
3. Deflated Sharpe Ratio
3.1 Deflated Sharpeとは
Bailey & Lopez de Prado (2014)が提案した補正方法で、以下を考慮:
- Multiple testing: テストした戦略数
- データ長: サンプルサイズ
- 歪度と尖度: リターン分布の非正規性
3.2 計算式
DSR = Φ[(SR - SR* × √(1 + (γ₃/6)×SR + ((γ₄-3)/24)×SR²)) /
√(1/N + (γ₃²/6)/N + ((γ₄-3)²/24)/N)]
Where:
Φ = 標準正規分布CDF
SR = サンプルSharpe
SR* = ベンチマークSharpe(multiple testingを考慮)
γ₃ = リターンの歪度
γ₄ = リターンの尖度
N = サンプルサイズ
SR*計算(期待最大Sharpe):
SR* = √(2 × ln(K)) × √(1/N) × (1 - γ × √(2 × ln(K)) + ...)
簡略化版:
SR* ≈ √(Var[SR]) × [(1-γ)×Z_K + γ×Z_K×exp(-Z_K²/2)]
Where Z_K = Φ⁻¹(1 - 1/K)
3.3 完全実装
import numpy as np
from scipy import stats
def deflated_sharpe_ratio(
returns: np.ndarray,
n_trials: int,
rf: float = 0.0,
periods_per_year: int = 252
) -> dict:
"""
Deflated Sharpe Ratioを計算
Parameters:
-----------
returns : リターン系列
n_trials : テストした戦略/パラメータ組み合わせ数
rf : リスクフリーレート
periods_per_year : 年率化係数
Returns:
--------
dict : 元のSharpe、DSR、p値などを含む
"""
n = len(returns)
excess = returns - rf / periods_per_year
# 基本統計
mu = excess.mean()
sigma = excess.std(ddof=1)
skew = stats.skew(excess)
kurt = stats.kurtosis(excess) # Excess kurtosis
# 年率Sharpe
sr = np.sqrt(periods_per_year) * mu / sigma
# 期待最大Sharpe(multiple testingに基づく)
if n_trials > 1:
z = stats.norm.ppf(1 - 1 / n_trials)
euler_gamma = 0.5772156649
sr_star = np.sqrt(1/n) * (
(1 - euler_gamma) * z +
euler_gamma * z * np.exp(-z**2 / 2)
) * np.sqrt(periods_per_year)
else:
sr_star = 0
# シャープレシオの標準誤差(非正規性を考慮)
# Note: scipy.stats.kurtosis()はEXCESS kurtosisを返す(すでに3を引いている)
# したがって、(kurt-3)/24ではなく、kurt/24を直接使用
sr_var = (1 + (skew/6)*sr + (kurt/24)*sr**2) / n
sr_std = np.sqrt(sr_var) * np.sqrt(periods_per_year)
# Deflated Sharpeテスト統計量
if sr_std > 0:
z_stat = (sr - sr_star) / sr_std
p_value = 1 - stats.norm.cdf(z_stat)
else:
z_stat = np.nan
p_value = 1.0
# DSR = 戦略Sharpeがベンチマークを有意に超える確率
dsr = 1 - p_value
return {
'sharpe_ratio': sr,
'expected_max_sr': sr_star,
'deflated_sr': dsr,
'p_value': p_value,
'z_statistic': z_stat,
'n_observations': n,
'n_trials': n_trials,
'skewness': skew,
'kurtosis': kurt,
'is_significant': p_value < 0.05
}
3.4 使用例
# 戦略リターンをシミュレート
np.random.seed(42)
daily_returns = np.random.normal(0.0005, 0.01, 252) # 1年のデータ
# 50のパラメータ組み合わせをテストしたと仮定
result = deflated_sharpe_ratio(daily_returns, n_trials=50)
print(f"Original Sharpe: {result['sharpe_ratio']:.2f}")
print(f"Expected Max Sharpe (50 trials): {result['expected_max_sr']:.2f}")
print(f"Deflated SR: {result['deflated_sr']:.2%}")
print(f"p-value: {result['p_value']:.4f}")
print(f"Is Significant: {result['is_significant']}")
4. 実践的ガイドライン
4.1 いつ心配すべきか
| 状況 | リスクレベル | 推奨事項 |
|---|---|---|
| サンプルSharpe < 1.0、テストした戦略 < 10 | 低 | おそらく有効、検証を継続 |
| サンプルSharpe 1.0-2.0、テストした戦略 10-100 | 中 | DSRを計算、過学習に注意 |
| サンプルSharpe > 2.0、テストした戦略 > 100 | 高 | ほぼ確実に過学習 |
| サンプルSharpe > 3.0 | 非常に高 | データエラーまたは先読みバイアスをチェック |
4.2 Sharpe Ratioの報告方法
間違ったアプローチ:
「私の戦略はSharpe 2.5です」
正しいアプローチ:
「3年の日次データ(756観測)に基づき、サンプルSharpeは2.5、 95%信頼区間[2.3, 2.7]、30のパラメータ組み合わせをテストした後、 Deflated SR = 0.92、p値 = 0.04」
4.3 Multiple Testingを軽減する方法
1. テストする戦略数を事前に決定
- テストする戦略のリストを書き留める
- 途中で戦略を追加しない
2. Out-of-sampleバリデーション
- 開発に使用しないデータの20-30%を確保
- このデータで最終戦略を一度だけテスト
3. Bonferroni補正
- 有意水準 = 0.05 / K
- K戦略はp < 0.05/Kで有意である必要
4. 全てのテストを記録
- 失敗した戦略も記録
- 真のn_trialsの計算に使用
5. よくある誤解
誤解1: より高いSharpeは常により良い
極めて高いSharpe(>3)は通常以下を意味します:
- データエラー(重複計算、先読み情報)
- 過学習
- 戦略キャパシティが小さすぎる
誤解2: 3年のデータでSharpeを正確に推定できる
3年のデータ(756日)の標準誤差は約0.04-0.05、つまり:
- 真のSharpe 1.0の戦略はサンプルSharpe 0.9-1.1を示す可能性
- Sharpe 1.0と1.2の戦略を区別することはほぼ不可能
誤解3: バックテストSharpe = ライブSharpe
バックテストは通常過大評価します:
- スリッページと市場インパクトを無視
- 微妙な先読みバイアス
- サバイバーシップバイアス
経験則: ライブSharpe ≈ バックテストSharpe × 0.5-0.7
6. Multi-Agent視点
Multi-agentアーキテクチャでは、シャープレシオの統計的問題に特別な注意が必要です:
7. まとめ
| 重要ポイント | 説明 |
|---|---|
| 推定誤差 | 1年のデータでSharpe標準誤差 ≈ 0.07-0.08 |
| Multiple testing | 100戦略をテスト、期待最大Sharpe ≈ 0.19(純粋なノイズ) |
| Deflated SR | Multiple testingを考慮した有意性テスト |
| 実践的アドバイス | 信頼区間を報告、DSRを計算、懐疑的であり続ける |
さらなる読み物
- Lesson 03: Math and Statistics Fundamentals - 統計の基礎
- Lesson 07: Backtest System Pitfalls - バックテストの他のバイアス
- Bailey, D. H., & Lopez de Prado, M. (2014). The Deflated Sharpe Ratio
- Lo, A. W. (2002). The Statistics of Sharpe Ratios