シャープレシオ(Sharpe ratio)は、リターンをリスク(ボラティリティ)で割った リスク調整後リターン の定番指標です。本記事では定義を整理し、日次データから年率シャープレシオを計算するまでの流れを Python でまとめます。

目次

  1. 定義
  2. 日次から年率への換算
  3. 評価の目安
  4. サンプルデータの準備
  5. 年率シャープレシオの計算
  6. 関数化
  7. ローリングシャープレシオ
  8. ソルティノレシオ(参考)

定義

シャープレシオは「超過リターンの平均」を「超過リターンの標準偏差」で割った値です。

SR=E[rrf]σ(rrf)\text{SR} = \frac{\mathbb{E}[r - r_f]}{\sigma(r - r_f)}
  • rr : 戦略・銘柄のリターン
  • rfr_f : リスクフリーレート(risk-free rate、無リスク資産の利回り)
  • σ\sigma : 標準偏差

リスクフリーレートは「同じ期間に何もせず置いておけば得られる利回り」の代理として、短期国債金利などを使います。日本円の運用では、近年の短期金利が極めて低いため rf0r_f \approx 0 と近似する文献も多くあります。

日次から年率への換算

日次データから年率シャープを出すときは、平均を 252 倍、標準偏差を 252\sqrt{252} 倍します。

SRannual=rˉdaily×252σdaily×252=SRdaily×252\text{SR}_{\text{annual}} = \frac{\bar{r}_{\text{daily}} \times 252}{\sigma_{\text{daily}} \times \sqrt{252}} = \text{SR}_{\text{daily}} \times \sqrt{252}

この変形は「リターンが i.i.d.(独立同分布)」という仮定で成り立つ近似です。実際のリターンには自己相関があるため厳密ではありませんが、実務の標準的な目安として使われます。

評価の目安

シャープレシオの絶対値は戦略・市場・期間に大きく依存するため、機械的な「合格ライン」はありません。それでも、よく言及される目安は次のとおりです。

年率シャープレシオ一般的な解釈
0 未満リスクフリーに劣る
0 〜 0.5リスクに見合ったリターンが弱い
0.5 〜 1.0平均的・実用範囲
1.0 以上良好
2.0 以上非常に高い(過剰最適化を疑う材料にもなる)

過去のバックテストで 3 や 4 を超える数値が出たときは、データリーク・過剰最適化・銘柄バイアスを最初に疑うのが安全です。

サンプルデータの準備

t 分布で日次リターン(1000 営業日)を生成します。実データを使う場合は、戦略の日次リターン列(#11-3「移動平均クロス戦略を 5 銘柄でバックテスト」strategy_ret のような列)に置き換えます。

import numpy as np
import pandas as pd
rng = np.random.default_rng(seed=11)
n_days = 1000
returns = pd.Series(
rng.standard_t(df=8, size=n_days) * 0.011 + 0.0004, # 日次平均をやや正に
index=pd.date_range("2022-01-04", periods=n_days, freq="B"),
name="ret",
)

年率シャープレシオの計算

リスクフリーレートを年率 0.5%(日本円短期金利の例)として、日次に変換した上で計算します。

TRADING_DAYS = 252
rf_annual = 0.005
rf_daily = rf_annual / TRADING_DAYS
excess = returns - rf_daily
mean_d = excess.mean()
std_d = excess.std(ddof=0)
sharpe_annual = (mean_d / std_d) * np.sqrt(TRADING_DAYS) if std_d > 0 else float("nan")
print(f"年率シャープレシオ: {sharpe_annual:.3f}")

ddof=0 は母標準偏差です。ddof=1 の場合と数値はほぼ変わりませんが、慣例の違いに注意します。

関数化

複数の戦略・銘柄で繰り返し使うため、関数にまとめます。

def annualized_sharpe(
returns: pd.Series,
rf_annual: float = 0.0,
trading_days: int = 252,
) -> float:
"""日次リターン Series から年率シャープレシオを返す。
Notes
-----
`ddof=0` で母標準偏差を取り、i.i.d. 仮定で sqrt(trading_days) 倍する。
"""
rf_daily = rf_annual / trading_days
excess = returns.dropna() - rf_daily
std = excess.std(ddof=0)
if std == 0:
return float("nan")
return (excess.mean() / std) * np.sqrt(trading_days)
print(annualized_sharpe(returns, rf_annual=0.005))

ローリングシャープレシオ

戦略の安定性を確認したい場合、ローリング窓でシャープを計算します。

WINDOW = 252 # 約 1 年
rolling_mean = (returns - rf_daily).rolling(WINDOW).mean()
rolling_std = (returns - rf_daily).rolling(WINDOW).std(ddof=0)
rolling_sharpe = (rolling_mean / rolling_std) * np.sqrt(TRADING_DAYS)
print(rolling_sharpe.tail())

特定期間だけ高いシャープが出ている戦略は、別の期間で機能しなくなる可能性が高い指標です。

ソルティノレシオ(参考)

シャープレシオは「上下両方の変動」をリスクと見なすため、上方変動も等しくペナルティになります。下方リスクのみを分母にした派生指標が ソルティノレシオ(Sortino ratio) です。

Sortino=E[rrf]σdown\text{Sortino} = \frac{\mathbb{E}[r - r_f]}{\sigma_{\text{down}}}

下方標準偏差 σdown\sigma_{\text{down}} は、リスクフリーレート(またはゼロ)を下回った日のリターンだけから計算します。

def annualized_sortino(returns: pd.Series, rf_annual: float = 0.0, trading_days: int = 252) -> float:
rf_daily = rf_annual / trading_days
excess = returns.dropna() - rf_daily
downside = excess[excess < 0]
dd_std = np.sqrt((downside ** 2).mean()) # 下方リスク
if dd_std == 0:
return float("nan")
return (excess.mean() / dd_std) * np.sqrt(trading_days)
print(annualized_sortino(returns, rf_annual=0.005))

注意点

  • 標準偏差ゼロの罠: ポジションを取らない期間が長いと標準偏差がゼロに近づき、シャープが発散します。if std == 0 のチェックが必須です
  • リスクフリーレートの設定: 円建ての場合は短期金利が低く、rf=0r_f = 0 で代用しても結果はほぼ変わりませんが、指標の比較対象がドル建ての場合は揃える こと
  • i.i.d. 仮定の限界: 自己相関の強い戦略(モメンタム戦略など)では、年率換算が過大評価になる傾向があります
  • 手数料・スリッページ込み: バックテストで使うリターン列は、コスト控除後にしてから計算します

生成AI へのプロンプト例

複数戦略の比較表を作りたいときの例です。

入力 DataFrame に次の列があります:
- date (datetime64)
- strategy (str)
- ret (float, 日次リターン、コスト控除後)
各 strategy について、次の列を持つ DataFrame を返す関数
sharpe_table(df, rf_annual=0.0) を書いてください。
戻り値の列:
- strategy
- n_days
- ann_return: 年率平均リターン (mean * 252)
- ann_vol: 年率ボラ (std * sqrt(252))
- sharpe: 年率シャープレシオ
- sortino: 年率ソルティノレシオ
要件:
- pandas 2.2 / numpy 系
- 標準偏差は ddof=0
- 標準偏差が 0 の戦略は NaN を返す
- docstring を日本語で書く

まとめ

  • シャープレシオは超過リターンを標準偏差で割った値
  • 日次から年率への換算は「日次シャープ × 252\sqrt{252}
  • 1.0 を超えれば良好、3.0 以上が出たら過剰最適化を疑う
  • 標準偏差ゼロ・i.i.d. 仮定の限界を意識して使う
  • ソルティノレシオは下方リスクのみを使う派生指標