リスク1単位あたりの報酬
ボラティリティ20%でリターン12%の戦略は、ボラティリティ5%でリターン6%の戦略より優れているとは限りません。シャープレシオは両者を同じ尺度に乗せます。無リスク金利を上回る平均リターンをボラティリティで割り、sqrt(期間数) で年率換算した (mean - rf) / std です。式は平均と分散のトピックで学びました。このトピックは、その使い方、つまりシャープレシオの読み方、改善の仕方、そして何より、どこまで信頼できるかを扱います。シャープ0.5は平凡、1は良好、2は優秀で疑わしい水準です。
ボラティリティは上昇も下落も同じに数えますが、思わぬ利益に文句を言う人はいません。ソルティノレシオは悪い側だけを残します。標準偏差を下方偏差 sqrt(mean(min(r - mar, 0)²)) に置き換えます(mar は最低許容リターン)。mar を上回った日は寄与が0ですが、平均には含まれます。大きなプラスの跳ねと小さな損失を持つ戦略は、シャープよりソルティノではるかに高く評価されます。
import math
import numpy as np
import pandas as pd
returns = np.array([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.006, -0.001, -0.010, 0.004, 0.002, -0.003])
sharpe = returns.mean() / returns.std(ddof=1) * math.sqrt(252)
print(round(sharpe, 3)) # 0.598
mar = 0.0 # a day below zero is a bad day
downside = math.sqrt(np.mean(np.minimum(returns - mar, 0) ** 2))
sortino = (returns.mean() - mar) / downside * math.sqrt(252)
print(round(downside, 5), round(sortino, 3)) # 0.00329 0.804 the up days no longer count as risk変化を見る
全期間で1つのシャープレシオでは経緯が見えません。ローリング・シャープレシオは直近 window 個のリターンで再計算するので、戦略の良い局面と悪い局面が見えます。pandas ではローリング平均をローリング標準偏差で割り、年率換算します。短い窓がいかに激しく跳ねるかに注目してください。1か月分のデータでは戦略の順位付けにまったく足りません。
series = pd.Series(returns)
rolling = series.rolling(5)
rolling_sharpe = rolling.mean() / rolling.std() * math.sqrt(252)
print(rolling_sharpe.round(2).tolist())
# [nan, nan, nan, nan, 1.89, 0.0, 6.96, 4.97, -3.66, 0.51, 0.51, -4.69] a 5-day Sharpe ratio is mostly noiseどれだけが運か
シャープレシオは推定値であり、推定には誤差があります。n 個のリターンで測った期間あたりのシャープ sr の標準誤差は約 sqrt((1 + 0.5 * sr²) / n) です。ここでの12日では、シャープ0.04に対して標準誤差が日次0.29あり、誤差は推定値の7倍です。年率換算でも同じ12日で 0.6 ± 4.6 になります。years 年にわたって観測した年率シャープ S のt統計量は S * sqrt(years) なので、0でないと95%確信するには (1.96 / S)² 年が必要です。シャープ0.5なら15年、シャープ1なら4年です。控えめな戦略の短いバックテストは何も証明しません。
per_period = returns.mean() / returns.std(ddof=1) # the per-day Sharpe ratio
std_error = math.sqrt((1 + 0.5 * per_period ** 2) / len(returns))
print(round(per_period, 4), round(std_error, 4)) # 0.0376 0.2888 the error is 7 times the estimate
print(round(std_error * math.sqrt(252), 3)) # 4.584 annualised: 0.6 +/- 4.6
# Years needed for an annual Sharpe ratio to reach t = 1.96
for annual in (0.5, 1.0, 2.0):
print(annual, round((1.96 / annual) ** 2, 1))
# 0.5 15.4
# 1.0 3.8
# 2.0 1.0# A yearly Sharpe of 1.0 observed over 4 years (1,008 days): how precise is it?
per_day = 1.0 / math.sqrt(252)
error_per_day = math.sqrt((1 + 0.5 * per_day ** 2) / 1008)
print(round(per_day, 4), round(error_per_day, 4), round(error_per_day * math.sqrt(252), 3)) # 0.063 0.0315 0.5 so 1.0 +/- 0.5- シャープ =
(mean - rf) / std * sqrt(期間数)。ソルティノは標準偏差を下方偏差sqrt(mean(min(r - mar, 0)²))に置き換える - ローリング・シャープ(
rolling.mean() / rolling.std())で変化を見る。短い窓はほぼノイズ - 標準誤差は期間あたり
sqrt((1 + 0.5 * sr²) / n)。年率シャープのt統計量はS * sqrt(years)で、シャープ1には約4年が必要
次はドローダウンです。投資家が実際に痛みを感じる、高値からの損失を扱います。
ラボ:採点される内容
sortino_ratioで悪い日だけをペナルティにする25点rolling_sharpeでレシオの時間変化を追う25点sharpe_standard_errorでシャープレシオに含まれる運の大きさを測る25点sharpe_t_statでそれが本物かを問う25点