背景知識: 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標準誤差SE95%信頼区間
1.0252 (1年)0.077[0.85, 1.15]
1.0756 (3年)0.045[0.91, 1.09]
2.0252 (1年)0.109[1.79, 2.21]
2.0756 (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の標準誤差

紙上演習:

テストした戦略数KSE = 0.063の時の期待最大Sharpe
100.063 × √(2×ln(10)) = 0.135
1000.063 × √(2×ln(100)) = 0.191
1,0000.063 × √(2×ln(1000)) = 0.234
10,0000.063 × √(2×ln(10000)) = 0.270

結論: テストする戦略が多いほど、「最良戦略」のサンプルSharpeは高くなります—全てがノイズでも。


3. Deflated Sharpe Ratio

3.1 Deflated Sharpeとは

Bailey & Lopez de Prado (2014)が提案した補正方法で、以下を考慮:

  1. Multiple testing: テストした戦略数
  2. データ長: サンプルサイズ
  3. 歪度と尖度: リターン分布の非正規性

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-100DSRを計算、過学習に注意
サンプル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 &lt; 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アーキテクチャでは、シャープレシオの統計的問題に特別な注意が必要です:

Multi-Agent System Sharpe Evaluation

7. まとめ

重要ポイント説明
推定誤差1年のデータでSharpe標準誤差 ≈ 0.07-0.08
Multiple testing100戦略をテスト、期待最大Sharpe ≈ 0.19(純粋なノイズ)
Deflated SRMultiple testingを考慮した有意性テスト
実践的アドバイス信頼区間を報告、DSRを計算、懐疑的であり続ける

さらなる読み物

この章を引用する
Zhang, Wayland (2026). 背景知識: シャープレシオの統計的な罠. In AIクオンツ取引:ゼロからイチへ. https://waylandz.com/quant-book-ja/Statistical-Traps-of-Sharpe-Ratio
@incollection{zhang2026quant_Statistical_Traps_of_Sharpe_Ratio,
  author = {Zhang, Wayland},
  title = {背景知識: シャープレシオの統計的な罠},
  booktitle = {AIクオンツ取引:ゼロからイチへ},
  year = {2026},
  url = {https://waylandz.com/quant-book-ja/Statistical-Traps-of-Sharpe-Ratio}
}