GARCH予測によるボラティリティターゲティングとトレーディング
本シリーズの最初の3部では、ボラティリティを予測する方法を学んだ。Part 1では単変量のGARCH(1,1)を構築し、Part 2ではGJRとStudent-tイノベーションでレバレッジ効果とファットテールを加え、Part 3ではDCC-GARCHで共分散行列全体を時間を通じてモデル化した。それぞれの終わりに、我々は一つの数字を出力した——明日の予測ボラティリティである。そして正直に言えば、そこで手を止めてしまった。まるで予測を作ること自体が目的であるかのように。
しかしそれは違う。ボラティリティ予測はP&Lではない。低いQLIKEスコアで報酬をもらった者は誰もいない。予測が価値を持つのは、それがなければ違う判断をしていたであろう決定——どれだけ買うか、いつ手仕舞うか、どれだけ資本を配分するか——をまさに変えるその瞬間だけである。予測がポジションを動かさないなら、その統計的な正確さは個人的な趣味にすぎない。
この最終部は、そのループを閉じることについてである。Part 1〜3で得た予測を、最もクリーンな意思決定であるボラティリティターゲティングに活用する——これはポートフォリオの実現ボラティリティが一定の目標値に達するようにポジションをサイジングする手法である。そして、このブログが何よりも重視すること、すなわち誠実な評価を行う。GARCHを、単純だが強力なベースライン(ローリング実現ボラティリティ、EWMA)と比較し、真のボラティリティを決して観測できないという事実に頑健な損失関数を用い、コストとルックアヘッドなしのウォークフォワード・バックテストを実行し、ボラティリティターゲティングが何をもたらし何をもたらさないかを率直に述べる。ネタバレをすると、これはアルファを生み出すよりもはるかに確実に、リスク調整後リターンを改善しドローダウンを抑制する。
なぜボラティリティターゲティングが適切なテストなのか
ボラティリティ予測を使うより洗練された方法——オプション価格付け、VaR限度額、動的ヘッジ——もあるが、ボラティリティターゲティングは他の賭けからの汚染を最小限に抑えて予測の価値を切り分けられる手法である。その考え方は一つの式に集約される。
シグナルが示す方向性(今は単に「ロング」とする)を持つリスク資産を保有するとする。固定ポジションの代わりに、エクスポージャーを予測ボラティリティに反比例させてスケールする。
ここで は次期のボラティリティ予測(時点 までのデータのみを用いて作成)、 は戦略が目指す年率ボラティリティ——たとえば15%や20%——である。モデルが穏やかな市場を予測すればレバレッジを1.0近く(あるいはそれ以上)にかけ、荒れた市場を予測すればポジションを縮小する。スケールされたポジションの実現ボラティリティは、一次近似で
となる。ただしこれは が成り立つ場合に限る。つまりこの手法全体の質は、予測 が次期の実際のボラティリティ にどれだけ近いかという一点にかかっている。より良い予測はより平坦な実現ボラティリティのプロファイルを生み、後で見るようにより良いシャープレシオを生む。これこそがこのテストが適切である理由である。予測は装飾ではなく、分母そのものなのだ。
なぜこれがシャープレシオを高めドローダウンを抑えるのか
ここで働いているのは二つの実証的事実である。
ボラティリティはリターンよりもはるかに予測可能である。 日次頻度において明日のBTCリターンの方向はコイントスに近いが、その大きさはそうではない。ボラティリティはクラスター化する——大きな値動きは大きな値動きに続く——というのがGARCHが存在する理由そのものである(Part 1ではこれをエンコードする分散のAR(1)構造を導出した)。1日先の分散に対する が0.4〜0.6であることは日常茶飯事だが、リターンに対する同じ数字であればルネサンス級のシグナルになるだろう。ボラティリティターゲティングは予測可能な量を活用し、予測不可能な量については中立を保つ。
シャープレシオは時間を通じて一定ではなく、ボラティリティが急上昇すると低下する。 暗号資産市場における高ボラティリティ局面——デレバレッジのカスケード、取引所の破綻、あらゆるものが30%ギャップする日々——は、リスク単位あたりのリターンが悪化する傾向にあり、良化するわけではない。予測ボラティリティが高いときにまさに機械的にエクスポージャーを削ることで、ドローダウンへの寄与が最も大きく複利リターンへの寄与が最も小さい局面のウエイトを下げることになる。Moreira and Muir (2017)は株式について、ボラティリティ管理型ポートフォリオ——まさにこの スケーリング——がシャープレシオを高め、無管理のファクターに対して正のアルファを生むことを示した。このメカニズムは魔法ではなく、予測可能に荒れる局面に定額ポジションを持ち続けることを拒否しているにすぎない。
ドローダウンへの効果はさらに直接的である。最大ドローダウンはポジションリターン分布のテールに支配される。 のボラティリティが 付近に固定されているため、ボラティリティが爆発する局面で固定ノーショナルの戦略が被る太い左テールは圧縮される——そもそも最初から小さいポジションだったからである。ボラティリティターゲティングはクラッシュを予測するわけではないが、市場が動揺しているときには体系的にアンダーエクスポージャーとなり、そして動揺こそがクラッシュの起きるときなのである。
KellyとフラクショナルサイジングとのRelationship
ボラティリティターゲティングはKelly基準の従兄弟にあたる。期待超過リターン と分散 を持つ単一資産について、成長最適(フルKelly)な配分比率は
シャープレシオ がおおむね一定であると仮定すれば——これは強い仮定だが「市場はリスクに対して安定した価格を支払う」という考え方に暗黙に含まれるものである—— となり となる。これはまさに としたボラティリティターゲティングそのものである。言い換えれば、ボラティリティターゲティングとは、期待リターンがボラティリティにスケールするという仮定の下でのKellyサイジングである。 フルKellyとフラクショナルKelly、そしてまともな人間が誰もフルKellyでトレードしない理由についてはKelly criterion and strategy sizingで扱っている。そこでの実践的な教訓はここにも当てはまる。すなわち、理論上のサイズの一部分だけを使うべきである。なぜなら分母(ボラティリティ予測)、そして特に分子(期待リターン)の推定誤差が、フルサイジングを危険なほど攻撃的にしてしまうからだ。
さらに二つの関連性を心に留めておく価値がある。第一に、ボラティリティターゲティングはリターンの対称的な散らばりに基づいてサイジングするが、暗号資産のペイオフは対称的ではない——レバレッジと清算が絡む場合、20%の下落日のコストは20%の上昇日の鏡像ではない。この非対称性についてはloss-profit asymmetryで直接扱っており、GJR/EGARCH予測(Part 2)はネガティブショックへより強く反応することで、その一部をすでに に織り込んでいる。第二に、ボラティリティ予測は点推定であり、より完全なリスク観にはそこに区間を付す必要がある。Conformal prediction for tradingでは、モデル出力を分布によらない区間に変換してサイジングに使う方法を示しており、本稿の誠実な評価というテーマと自然に対をなす。
対抗馬たち — GARCHが打ち破らねばならないもの
ここが、実演とデモを本物の評価と分けるための規律である。GARCHに冠を授ける前に、安価で自明でありながら意外に打ち破りにくい対抗馬を与えなければならない。もし精緻なGJR-tモデルが5行のEWMAに勝てないなら、それは価値ある学びを得て、プロダクション上の複雑さを大きく節約したことになる。
ここでは次期ボラティリティを予測する4つの手法をベンチマークする。
(a) トレーリング実現ボラティリティ(ローリング標準偏差)
最も素朴な予測——明日のボラティリティは直近 日間の日次リターンの標本標準偏差に等しいとする。
ハイパーパラメータはウィンドウ (通常20〜60日)一つだけで、モデルは存在しない。その欠点は、ウィンドウ内のすべての観測値が等しいウェイトを持ち、そのウィンドウから外れた瞬間に急に崖から落ちることである——いわゆる「ゴースティング」あるいは「エコー」効果であり、市場が実際に落ち着いたかどうかにかかわらず、単一のクラッシュ日がちょうど 日間予測を膨らませ、そして一夜にして消え去る。
(b) EWMA / RiskMetrics ()
指数加重移動平均は、過去の二乗リターンに幾何級数的に減衰するウェイトを与えることでエコー問題を修正する。
これはRiskMetrics(J.P. Morgan、1996年)の推定量である。日次データに対する標準的な を用いると、実効的な記憶はおよそ 日となるが、その減衰は滑らかであり崖はない。EWMAが実際には何であるかに注目してほしい——それは** かつ の積分型GARCH(1,1)**、すなわち平均回帰も長期分散も持たないGARCHである。 を受け入れるなら自由パラメータはゼロであり、この記事全体を通して最も打ち破りにくい単一のベースラインである。「GARCHがXに勝つ」と主張する論文の相当な割合が、実はアウトオブサンプルでEWMAに静かに負けている。
import numpy as np
import pandas as pd
def ewma_vol(returns: pd.Series, lam: float = 0.94, sigma0: float | None = None) -> pd.Series:
"""RiskMetrics EWMA conditional volatility (returns in decimal, e.g. 0.03).
sigma2_t = lam * sigma2_{t-1} + (1 - lam) * r_t^2
Returns a series aligned to `returns` where value at t uses info up to t.
"""
r2 = np.square(returns.values)
var = np.empty_like(r2)
var[0] = sigma0**2 if sigma0 is not None else r2[0]
for t in range(1, len(r2)):
var[t] = lam * var[t - 1] + (1.0 - lam) * r2[t - 1] # note: r_{t-1}, no look-ahead
return pd.Series(np.sqrt(var), index=returns.index, name="ewma_vol")
多くの人がつまずく微妙な点は次の通りである——期間 のための予測(期間 にわたって保有されるポジションのサイジングに使える予測)は、 より前に観測されたリターンから構築されなければならない。上の再帰式では var[t] が r2[t-1] を使っているので、この系列は正真正銘の1ステップ先予測である。このインデックスを正しくすることが、バックテストと空想の違いを分ける——詳細はウォークフォワードの節で述べる。
(c) GARCH(1,1)とGJR-t(Part 1〜2)
我々の主役たちである。標準的なGARCH(1,1)は
であり、長期分散は で、1ステップ予測はこの再帰式(Part 1)からそのまま得られる。GJR-GARCH拡張はレバレッジ項を加え、ネガティブショックがポジティブショックよりも分散を大きく引き上げるようにする。
これをStudent-tイノベーションと組み合わせて暗号資産のファットテールを扱ったものが、Part 2のGJR-tである。GARCHがEWMAに勝ち得る理由は平均回帰にある——ショックの後、GARCHは で決まる速度で予測を に引き戻すが、EWMAは(積分型であるため)決して回帰しない。ボラティリティが急上昇してから正常化する——よくあるケース——場合、GARCHの予測はより速く、より正確に戻る。ボラティリティが真に持続的である場合、両者はほとんど区別がつかない。
(d) 実現分散に対するHAR-RV(イントラデイデータがある場合)
イントラデイのバー——そして24/7の暗号資産市場ではほぼ常にそうだが——があれば、日次二乗リターンよりもはるかにノイズの少ないボラティリティのプロキシを構築できる。すなわち実現分散、つまりその日のイントラデイリターンの二乗和である。
Corsi (2009)のHeterogeneous Autoregressiveモデルは、明日の実現分散を過去の の日次・週次・月次平均から予測する——長期記憶の持続性を3つの回帰変数で捉える、粗いが驚くほど効果的な方法である。
ここで と は日次 の直近5日および22日のトレーリング平均である。これは単純なOLS回帰であり、より高品質なイントラデイのプロキシを活用しており、4つの手法の中で最良の日次ボラティリティ予測手法であることが多い——しばしばGARCHに勝つのは、まさに が よりクリーンなターゲットであるからである。
import numpy as np
import pandas as pd
def realized_variance(intraday_returns: pd.Series, day_index) -> pd.Series:
"""Daily realized variance = sum of squared intraday (log) returns per day.
`intraday_returns` indexed by timestamp; `day_index` maps to calendar day.
"""
return intraday_returns.pow(2).groupby(day_index).sum()
def har_features(rv: pd.Series) -> pd.DataFrame:
"""Build HAR regressors from a daily realized-variance series."""
df = pd.DataFrame({"rv": rv})
df["rv_d"] = df["rv"].shift(1) # yesterday
df["rv_w"] = df["rv"].shift(1).rolling(5).mean() # trailing week
df["rv_m"] = df["rv"].shift(1).rolling(22).mean() # trailing month
df["target"] = df["rv"] # predict today's RV
return df.dropna()
def fit_har(rv: pd.Series):
"""Fit HAR-RV by OLS. Returns (coef_dict, predict_fn)."""
df = har_features(rv)
X = np.column_stack([np.ones(len(df)), df["rv_d"], df["rv_w"], df["rv_m"]])
y = df["target"].values
beta, *_ = np.linalg.lstsq(X, y, rcond=None)
names = ["const", "rv_d", "rv_w", "rv_m"]
def predict(rv_d, rv_w, rv_m):
return float(beta @ np.array([1.0, rv_d, rv_w, rv_m]))
return dict(zip(names, beta)), predict
log-HARについて一言。 は右に歪んでおり厳密に正であるため、多くの実務家はを対数のHAR特徴量に回帰させる。これはフィットを改善し予測値の正値性を保証する。指数を戻す際には半分散のJensen補正、、を加えるべきである。そうしないと体系的に予測が過小になる。
4つの予測手法——RV、EWMA、GARCH/GJR-t、HAR——が手元に揃ったところで、問題は次のようになる。それらすべてが予測しようとしている対象を決して観測できないとき、どうやってどれが最良かを判断すればよいのか?
ボラティリティ予測を誠実に評価する
ここが本稿の核となる教えなので、ゆっくり進もう。
予測 を真の条件付き分散 と比較したい。しかし は潜在変数であり——データ生成過程のパラメータであって、直接観測されることは決してない。得られるのは1日に1つの実現リターンだけである。したがって、あらゆるボラティリティ評価は実際には、あなたの予測を真の値のノイズを含むプロキシと比較しているにすぎない。標準的なプロキシは2つある。
- 二乗リターン 。ゼロ平均モデルの下では に対して不偏である()が、極めてノイズが多い——1日分のリターンは標準偏差の1観測分の推定量にすぎない。プロキシ は、真のボラティリティが高くても平坦な日には0になり得るし、幸運なテール日には巨大にもなり得る。
- イントラデイデータから得られる実現分散 。はるかにノイズが少ない——イントラデイのサンプリングが単一リターンの特異的ノイズを平均化するためである——これこそがHAR-RVが機能する理由であり、イントラデイデータがあるならプロキシとして を使うべき理由である。
ほぼ全員がつまずく微妙な点はこうだ。プロキシがノイズを含むため、損失関数の選択は無害ではない。 誤った損失関数で2つの予測を順位付けすると、ノイズを含むプロキシが順位を逆転させ、劣った予測の方が良いと告げてくることがある。Patton (2011)は、ノイズを含むプロキシに対する期待損失による予測の順位付けが、真の(観測不能な)分散に対する順位付けと同じ結果を与えるという意味で「頑健」な損失関数を厳密に導出した。その条件を満たすのは特定のファミリーだけである。実務上重要なのは2つのメンバーである。
MSE対QLIKE
分散に対する平均二乗誤差は
ここで は予測値、 は または でプロキシされる。MSEはPattonの意味で頑健(その順位付けはプロキシに対して一貫している)だが、対称的かつスケール依存である——同じ絶対値の過大予測と過小予測を等しくペナルティし、高ボラティリティ期間の誤差を穏やかな期間の誤差よりもはるかに重くウェイト付けする。穏やかな95%の日をうまく捉えつつ、3日間の危機的な分散予測で大きく外すモデルは、MSEの下ではひどく見えるだろう——たとえその危機時の挙動こそが本当に望まれるものであっても。
QLIKE(準尤度)損失はこの分野の主力である。
これは分散に対するガウス尤度から導かれる損失であり、Pattonの意味でも頑健であり、さらにボラティリティ評価の選択として好まれる2つの性質を持つ。第一に、正しい方向に非対称である——分散の過小予測を過大予測よりも強くペナルティする。リスクマネージャーやボラティリティターゲッターにとって、これは正しい非対称性である。ボラティリティの過小予測とは、それが重要になる直前に過大なサイズを取っていたということであり、これは高くつく誤りである。第二に、(おおむね)スケール不変である——比 に依存するため、10%の予測誤差は穏やかな日でも危機の日でもほぼ同じコストとなり、MSEのように少数の高分散観測に評価が乗っ取られることがない。プロキシの不均一分散に対するこの頑健性こそ、そもそもボラティリティが激しく変動するという話の全体において望ましいものである。
であり、等号は の場合のみ成立する点に注意されたい。MSEと同様、小さいほど良い。
import numpy as np
def qlike(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
"""Per-observation QLIKE loss. `proxy` is r^2 or RV (a variance proxy),
`forecast_var` is h = sigma_hat^2. Both strictly positive, same units.
"""
ratio = proxy / forecast_var
return ratio - np.log(ratio) - 1.0
def mse_var(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
"""Per-observation MSE on the variance scale."""
return np.square(proxy - forecast_var)
運用上の警告を2つ。プロキシと予測値は同一の単位(どちらも日次分散、あるいはどちらも年率化)に揃えること。そうしなければ比は無意味になる。そして forecast_var を決してゼロにしないこと——小さな下限でクリップしておく必要がある。さもないと が平均全体を汚染する。
Mincer-Zarnowitz回帰
単一の損失の数値はどちらの予測が優れているかを教えてくれるが、予測がどのように間違っているかは教えてくれない。Mincer-Zarnowitz (1969)回帰はそれを教えてくれる。プロキシを予測値に回帰させる。
最適で不偏な予測の下では かつ となる——平均して実現分散は予測値に等しい。この乖離が病理を診断してくれる。
- を伴う : 過度にボラティリティに反応する予測の典型的な兆候である——過剰反応し、実際には十分に実現しない極端な値を予測している。二乗リターンに直接駆動されるモデルでよく見られる。
- : 予測が過小反応しており、真の分散に対するスケーリングが不十分である。
- 回帰 が低い: たとえ が平均的には問題なく見えても、予測は日々の分散をうまく追跡できていない。プロキシが非常にノイズが多いため、 に対するMZのがしばしば0.05〜0.20程度しかなくても驚くにはあたらない—— に対してであればはるかに高くなる。 に対する は、予測がどれほど優れていても、プロキシのノイズのために純粋に1をかなり下回るところで頭打ちになる。
の同時 検定は正式なキャリブレーション検査を与える。実務上は、MZを予測を理解するための診断として用い、QLIKEを予測を順位付けするために用いる。
Diebold-Mariano — その差は本物か?
GARCHの平均QLIKEが0.183で、EWMAが0.191だったとしよう。GARCHが「勝った」ことになる。しかし0.008という差は本物の優位性なのか、それともサンプリングノイズなのか?Diebold-Mariano (1995)検定はまさにこれに答える。期間ごとの損失差分を
と定義する(2つの予測 と について、ここでは = QLIKE)。帰無仮説は予測精度が等しいこと、 である。統計量は が系列相関を持つため、その長期(HAC)標準誤差で標準化した平均差分である。
ここで はNewey-West型の長期分散推定量である。DM統計量が を超えると、有意水準5%で精度が等しいという帰無仮説を棄却する。重要なのは、DMは予測に関する検定であり、入れ子になったモデルについての検定ではないという点、そして単純な に対する 検定が無視してしまうであろう損失系列の系列依存性を扱っているという点である。
import numpy as np
from scipy import stats
def diebold_mariano(loss_a: np.ndarray, loss_b: np.ndarray, h: int = 1):
"""Diebold-Mariano test of equal predictive accuracy.
loss_a, loss_b: per-period losses (e.g. QLIKE) for forecasts A and B.
h: forecast horizon; Newey-West lag = h - 1 (>=0), plus a small-sample buffer.
Returns (DM_stat, p_value). Negative DM => A has lower loss (A better).
"""
d = np.asarray(loss_a) - np.asarray(loss_b)
T = len(d)
d_bar = d.mean()
lag = max(h - 1, 0)
gamma0 = np.mean((d - d_bar) ** 2)
lrv = gamma0
for k in range(1, lag + 1):
w = 1.0 - k / (lag + 1)
cov = np.mean((d[k:] - d_bar) * (d[:-k] - d_bar))
lrv += 2.0 * w * cov
dm = d_bar / np.sqrt(lrv / T)
adj = np.sqrt((T + 1 - 2 * h + h * (h - 1) / T) / T)
dm *= adj
p = 2.0 * (1.0 - stats.t.cdf(abs(dm), df=T - 1))
return float(dm), float(p)
そのコメント中の例示的な結果は、誠実かつありふれた結末であり、この節が存在する理由そのものである。GARCHはしばしばEWMAよりわずかに低い平均損失を記録するが、同じくらいの頻度で、その優位性はDMの有意性の基準をクリアできない。もし平均QLIKEだけを報告し続ければ、DM検定であれば却下されたはずの優位性を自ら信じ込んでしまうことになる。DM統計量を報告すること。これは、honest evaluation with no robust edgeで戦略リターンに適用しているのと同じ規律である——ベンチマークを上回る点推定は、それがノイズではないと除外できるまではエッジではない。
バックテスト — ウォークフォワード方式のボラティリティターゲット戦略
ここで2つの半分——予測手法とサイジング規則——を組み合わせて一つの戦略にし、意味のある唯一の方法で評価する。ウォークフォワード、アウトオブサンプル、コスト込みである。
この戦略は意図的にシンプルである。というのも、シンプルさこそが、結果を巧妙なシグナルではなくボラティリティ予測に帰属させることを可能にするからである。ロングオンリー、ボラティリティターゲット型のBTC。 毎日、翌日のボラティリティを予測し、ポジションを に設定し、翌日まで保有し、これを繰り返す。ロング/フラットのバリアントはトレンドフィルターがネガティブなときにポジションをゲートオフする。小規模ポートフォリオのバリアントは、単一資産の分散ではなくPart 3のDCC共分散に基づいてサイジングする。ここではロングオンリーのケースを詳細に説明し、拡張については補足する。
ウォークフォワードの仕組みとルックアヘッド禁止契約
このバックテストにおいて最も重要な性質は、日 のポジションサイジングに使われるすべての量が、日 の終値時点で入手可能なデータのみを用いて計算可能であるということである。GARCHパラメータは で終わるローリングウィンドウ上で再推定され、予測はそのフィットから得られる1ステップ先の であり、ポジションはその予測から設定され、獲得されるリターンは である(ここで はモデルが決して見なかった次の日のリターンである)。全サンプルでGARCHを再フィットしてから過去を「予測」することは、人々が誤って素晴らしいバックテストを捏造してしまう最もありふれた方法である。このルックアヘッドの罠と一般的な方法論についてはwalk-forward optimizationで扱っている。
import numpy as np
import pandas as pd
from arch import arch_model
def walk_forward_voltarget(
returns: pd.Series, # daily log returns in decimal (e.g. 0.021)
proxy_var: pd.Series, # variance proxy aligned to returns (RV or r^2)
target_vol_annual: float = 0.20,
window: int = 750, # rolling estimation window (days)
refit_every: int = 5, # refit GARCH weekly, forecast daily (cost saver)
w_max: float = 3.0, # leverage cap
cost_bps: float = 5.0, # per-unit-turnover cost in basis points
ann: int = 365, # crypto trades 365 days/yr
):
"""Long-only vol-targeted BTC, GARCH(1,1)-t forecasts, strictly walk-forward.
Returns a DataFrame with position, forecast vol, net returns, and turnover.
"""
idx = returns.index
target_daily = target_vol_annual / np.sqrt(ann)
fcast_vol = pd.Series(index=idx, dtype=float, name="fcast_vol")
last_res = None
for i in range(window, len(returns) - 1):
if (i - window) % refit_every == 0 or last_res is None:
train = returns.iloc[i - window:i + 1] * 100.0 # scale for the optimizer
am = arch_model(train, mean="Constant", vol="GARCH",
p=1, o=1, q=1, dist="t") # GJR-t, Part 2
last_res = am.fit(disp="off")
f = last_res.forecast(horizon=1, reindex=False)
var_next = f.variance.values[-1, 0] / (100.0 ** 2) # unscale
fcast_vol.iloc[i + 1] = np.sqrt(var_next)
raw_w = (target_daily / fcast_vol).clip(upper=w_max)
position = raw_w.shift(0) # w_{i+1} known at close i
gross_ret = position * returns # earned on day i+1
turnover = position.diff().abs().fillna(0.0)
cost = turnover * (cost_bps / 1e4)
net_ret = (gross_ret - cost).dropna()
out = pd.DataFrame({
"position": position,
"fcast_vol": fcast_vol,
"gross_ret": gross_ret,
"turnover": turnover,
"net_ret": net_ret,
}).dropna()
return out
def performance_stats(net_ret: pd.Series, ann: int = 365) -> dict:
"""Sharpe, realized vol, max drawdown, CAGR, annual turnover."""
mu = net_ret.mean() * ann
sigma = net_ret.std() * np.sqrt(ann)
sharpe = mu / sigma if sigma > 0 else np.nan
equity = (1.0 + net_ret).cumprod()
peak = equity.cummax()
max_dd = (equity / peak - 1.0).min()
cagr = equity.iloc[-1] ** (ann / len(net_ret)) - 1.0
return {
"sharpe": round(sharpe, 2),
"realized_vol": round(sigma, 3),
"max_drawdown": round(max_dd, 3),
"cagr": round(cagr, 3),
}
見た目以上に重要な実装上の注意点をいくつか挙げる。
- オプティマイザ向けのスケーリング。
archのフィットは、リターンがパーセント単位のときに数値的に安定する。そのため* 100と、分散を元に戻す際の対応する/ 100**2が必要である。これを忘れると、目標ボラティリティが10,000倍ずれてしまう。 - 再フィットの頻度。 GARCHパラメータを毎日再推定するのはコストが高く、ほとんど何ももたらさない——パラメータは週単位で安定している。週次で再フィットしつつ(
refit_every=5)、日次で予測する(再フィットなしでも新しいリターンから を更新する再帰式による)のが標準的な妥協点である。これはcopula models for joint riskのコピュラパイプラインにおけるキャッシュのアドバイスと呼応する。 - 上限 は装飾ではない。 完全に凪の局面で予測ボラティリティが崩壊すると、 は5倍、10倍のレバレッジに爆発しかねない。上限のないボラティリティターゲティングは、ボラティリティレジームが変化する直前——予測が最も外れやすいまさにその瞬間——に喜んで壊滅的なレバレッジを与えてしまう。上限を設け(ここでは3倍)、その上限が最も穏やかで、後から振り返れば最も危険な局面でまさに効いてくることを認識しておくこと。
- コストは回転率に比例し、ボラティリティターゲティングは回転率を生み出す装置である。 予測のわずかな揺らぎのたびにポジションが再サイジングされる。低ボラティリティ資産で予測が神経質だと、毎日ブックを回転させることになりかねない。
cost_bpsの項は些末な丸め処理ではない——高回転率のボラティリティターゲットにとって、これはグロスのシャープ改善の相当な部分を食いつぶし得る。
出力の見え方(例示)
これをBTCの日次データで複数年のウィンドウにわたって実行し、4つの予測手法をサイジングの分母として比較すると、次のような形状の表になる傾向がある。以下の数値は例示であり——典型的なパターンを示すために手で選んだものであって実際のバックテストの出力ではない——しかし、その順序と大きさは実務家が報告する典型を代表している。
| サイジングに用いる予測 | シャープ | 実現ボラティリティ | 目標ボラティリティ | 最大ドローダウン | 年間回転率 |
|---|---|---|---|---|---|
| 固定ノーショナル(ターゲティングなし) | 0.71 | 0.68 | — | -0.78 | 0.1x |
| ローリングRV(60日) | 0.94 | 0.24 | 0.20 | -0.41 | 12x |
| EWMA () | 1.02 | 0.21 | 0.20 | -0.35 | 19x |
| GARCH(1,1)-t | 1.05 | 0.21 | 0.20 | -0.33 | 22x |
| HAR-RV(イントラデイのプロキシ) | 1.09 | 0.20 | 0.20 | -0.31 | 20x |
構築する価値のある2つのバリアント
ロングオンリーのケースは予測を切り分けるものだが、明示的に示す価値のある拡張が2つある。
トレンドゲート付きロング/フラット。 ボラティリティターゲティングはポジションをサイジングするが、方向性については見解を持たない——常にロングである。安価で誠実な改善策は、遅行するトレンドフィルターがネガティブになったときにポジションをゲートオフすることであり、そうすればボラティリティターゲット型のロングをアップトレンドでのみ保有し、それ以外ではフラットになる。これはサイジングロジック自体は同一に保ちつつ、その上に粗いレジームフィルターを重ねるものであり、エントリーのタイミングを計るふりはせず、明白なダウントレンドを保有し続けることを避けるだけである。
def apply_trend_gate(position: pd.Series, price: pd.Series, fast: int = 20, slow: int = 100):
"""Zero out the (already vol-targeted) position when the fast SMA is below
the slow SMA. Both SMAs use only past prices, so no look-ahead is introduced.
"""
fast_ma = price.rolling(fast).mean().shift(1) # shift: known at prior close
slow_ma = price.rolling(slow).mean().shift(1)
gate = (fast_ma > slow_ma).astype(float) # 1.0 in uptrend, 0.0 otherwise
return position * gate.reindex(position.index).fillna(0.0)
トレンドゲートは下振れ局面での回転率を削減する(弱気相場で縮小しつつあるポジションを回転させ続けなくて済む)が、それ自体のレジームリスクを追加する——もみ合いの横ばい相場ではダマシに遭い、転換点では遅れる。それが役立つかどうかは、サイジングルール自体と同じウォークフォワード・DM検定の厳密さで答えなければならない実証的な問いである。トレンドフィルターはまさに、インサンプルでは素晴らしく見えてアウトオブサンプルで消え失せる類の付加要素である。
DCC共分散に対するポートフォリオ・ボラティリティターゲティング。 複数資産のブックについては、スカラーの予測 がポートフォリオボラティリティ に置き換わる。ここで はPart 3のDCC-GARCHから得られる時変共分散行列である。ベースウェイト (等ウェイト、時価総額加重、あるいは平均分散のティルト)を選び、 の下でのポートフォリオの予測ボラティリティを計算し、ポートフォリオの目標値に達するようにウェイトベクトル全体をスケールする。
def portfolio_voltarget_weights(base_w, cov_forecast, target_vol_daily, w_gross_max=3.0):
"""Scale a base weight vector so forecast portfolio vol hits the target.
base_w: (n,) base allocation (need not sum to 1).
cov_forecast: (n, n) one-step-ahead covariance from DCC-GARCH (daily units).
"""
base_w = np.asarray(base_w, dtype=float)
port_var = float(base_w @ cov_forecast @ base_w)
port_vol = np.sqrt(max(port_var, 1e-12))
scale = min(target_vol_daily / port_vol, w_gross_max / np.abs(base_w).sum())
return scale * base_w
これはポートフォリオ構築の文献への自然な架け橋である。ベースウェイト はMarkowitz mean-varianceやHRP/CVaRのようなリスクベース手法から得ることができ、その上にボラティリティターゲティングがオーバーレイとして乗り、集約リスクを一定にスケールする。DCC行列が重要なのは、相関がクラッシュ時に急上昇するからである(Part 3)——穏やかな市場では分散されて見えるポートフォリオが、まさに重要な局面において、静的な共分散が示唆するよりもはるかに高い予測ボラティリティを持つことがあり、このオーバーレイがそれに応じてグロスエクスポージャーを削る。
常にプロットすべき診断
サマリーの表だけを信用してはいけない。どんなボラティリティターゲットについても、次の3つをプロットし、目視で確認してから初めてどんなシャープの数値も信じるべきである。第一に、戦略のローリング実現ボラティリティを目標線と重ねてプロットする——目標線に沿うべきであり、それを継続的に上回るドリフトは予測が低めにバイアスしていることを意味する(これは高くつく方向である)。第二に、ポジション/レバレッジの系列——上限が効いている箇所や、ドローダウンの直前にレバレッジが急上昇している箇所を探す。これはレジーム変化に不意を突かれた予測の兆候である。第三に、予測対プロキシの散布図(Mincer-Zarnowitzの図)——傾きが1から大きく外れた点の雲は、予測が誤ってスケールされていることを示すが、これはQLIKEの平均値では隠れてしまいかねない。この3つのプロットは、単一の統計量よりも多くのバグと多くの自己欺瞞を捉えてくれる。
この表は、あらゆるバックテストの表を読むべきやり方で読むべきである。すなわち、何が頑健で何が限定的かを見極めることである。頑健な事実は際立っている。すべてのボラティリティターゲット型バリアントは、シャープにおいて固定ノーショナルを圧倒し、さらに劇的に、ドローダウンと実現ボラティリティの安定性においても圧倒する——固定ノーショナル戦略は年率68%のボラティリティで78%のドローダウンを記録しており、これは端的に投資不可能である。そしてすべてのターゲティング手法が20%の目標に近い実現ボラティリティを達成している——これはメカニズムが機能しているというまさにその約束そのものである。限定的な事実は予測手法間の差である。HARがGARCHをわずかに上回り、GARCHがEWMAをわずかに上回り、EWMAがローリングRVをわずかに上回るが、その差は小さく——シャープポイントの10分の1程度——予測に対してDiebold-Marianoで、あるいはリターンに対してブートストラップで検定すれば、しばしば有意性をクリアできないだろう。この、洗練された予測手法と素朴な予測手法の間にある小さく脆く、レジームに依存する差こそが、このシリーズ全体の誠実な見出しである。
これが何をもたらすかについて正直であること
このブログには、自分を欺かずにバックテストを行うことについての一連の記事がある。それを我々自身の結果に、こっそりと期待するのではなく、実際に適用してみよう。
ボラティリティターゲティングはリスク調整後リターンとドローダウンを改善する。無から アルファを生み出すわけではない。 もう一度あの表を見てほしい。ターゲティングによるシャープの改善は本物であり、得る価値がある——しかしそれを分解すると、その大部分は高ボラティリティのレジームに定額ポジションを持ち続けないことから来ており、これは機械的に最悪のドローダウンを回避し、複利のパスを安定させる。この戦略は依然としてBTCのロングであり、市場が与えていない見解を持っているわけではない。もしサンプル期間全体でBTCのシャープが負であれば、ボラティリティターゲティングはより悪くない負のシャープを与えるのであって、正のシャープを与えるわけではない。それはリターン分布の形を変える——テールを薄くし、ボラティリティを安定させ、幾何複利をより良くする——が、生の方向性のエッジはあくまで元となるロングポジションが持つものそのものである。美しいエクイティカーブに騙されて、実際にはリスクマネジメントを見つけただけなのにアルファを見つけたと信じ込んではならない。Moreira-Muirは株式ファクターにおいてボラティリティ管理から真のアルファを見出したが、その結果はファクターの時変リスク・リターントレードオフに関するものであり、異なるサンプルの単一の暗号資産に自動的に移転するわけではない。
GARCHのEWMAに対する予測品質のエッジは、しばしば小さくレジームに依存する。 これがPart 1〜3の居心地の悪い代償である。あなたはレバレッジ項、Student-tテール、動的相関という、ますます洗練されたモデルを構築してきたが、それぞれの素朴なEWMAに対するボラティリティターゲティングP&Lへの限界的な寄与は、しばしばノイズの帯域内に収まってしまう。GARCHの優位性(ショック後の平均回帰)は、主に特定のレジーム——急上昇してから正常化する局面——で現れる。だらだらとしたトレンドや持続的な高ボラティリティのレジームでは、EWMAとほとんど変わらない。これはGARCHを無価値にするわけではない——平均回帰の構造、解釈可能なパラメータ、先行きのパスをシミュレートしその予測に対してオプションを価格付けする能力は、いずれもEWMAが持たない価値である——しかし、あなたの唯一の用途がサイジングであるなら、複雑さのコストを払う前にDM検定を実行すべきであり、またregime detectionが別の角度から同じことを告げていると知っておくべきである。すなわち、勝つモデルはレジームに依存するということである。
バックテストのシャープはライブのシャープの上限であり、ボラティリティターゲティングはその差を広げる。 この戦略は回転率が高くレバレッジがスケールされるため、素朴なバックテストが見落とすフリクションに対して異常に敏感である——約定はサイジングの根拠にした終値よりも悪く、レバレッジをかけたパーペチュアルポジションのファンディングコストは継続的に発生し、レバレッジの上限は単純な w * return が無視するマージンと清算のメカニクスと相互作用する。これらのいずれもが、ライブをバックテストより悪化させる。この差についてはbacktest-live parityで体系的に扱っている。ボラティリティターゲティングに特有の対策としては、保守的なコストを用い、現実的な(低い)レバレッジ上限を用い、シグナルを計算した終値ではなく次のバーの始値で執行することで、この差を見込んでおくべきである。
ボラティリティリスクプレミアムについて一言。 上記のすべては実現ボラティリティを予測するものである。これと並行して、トレード可能な対象がある——オプションに価格付けされるインプライドボラティリティであり、これは平均して後続の実現ボラティリティを上回る水準にある——ボラティリティ急騰リスクを負担することへの補償であるボラティリティリスクプレミアムである。このギャップ自体がリターンの源泉となり得る(分散を売ればそれを刈り取り、買えばテールリスクをヘッジする)、これはボラティリティターゲティングとは本質的に異なるゲームである——ボラティリティの予測を利用するのではなく、ボラティリティの価格に賭けるのである。本稿ではこれを深追いしないが、その仕組みはBlack-Scholes options pricingの価格付けモデルから始まり、良質な実現ボラティリティ予測(Part 1〜2)は、インプライドボラティリティが割高か割安かを判断するためにまさに必要な入力である。あなたのGARCH予測をオプション市場のインプライドボラティリティと比較することは、このシリーズで構築したものすべての最も誠実な使い道の一つである。
実務上の考慮事項
機能するボラティリティターゲットと脆弱なそれとを分ける事柄をいくつか挙げる。
- 適切なホライズンで推定すること。 1日保有するポジションをサイジングするなら1日先のボラティリティを予測せよ。週次でリバランスするなら、週次のボラティリティを予測(かつターゲティング)するか、日次のGARCH予測をそのホライズンにわたって集計せよ——マルチステップのGARCH予測は に向かって平均回帰するが、素朴な「」スケーリングはこれを無視する。マルチステップGARCH予測についてはPart 1で扱っている。
- 24/7市場における年率化。 暗号資産は週末や祝日なく365日取引されるので、日次ボラティリティを年率化する際には株式の ではなく を使うこと。これを誤ると、目標値が気づかぬうちに約20%ずれる。
- 分母は共分散行列でもよい。 複数資産のブックについては、スカラーの をPart 3のDCC-GARCHから得られるポートフォリオボラティリティ に置き換え、ウェイトベクトル全体をポートフォリオの目標ボラティリティに達するようスケールせよ。これはボラティリティターゲティングを、平均分散サイジング(Markowitz for crypto)やリスクベースの配分(HRP and CVaR pipelines)と結びつける——ボラティリティターゲティングは、ポートフォリオのリスク予算にスケールすることの単一資産版の特殊ケースである。
- ボラティリティターゲティングは、微妙な形でプロシクリカルである。 誰もが同じ ルールを走らせているとき、ボラティリティの急騰は同期したデレバレッジを強いる。これが価格を押し下げ、実現ボラティリティを押し上げ、さらなるデレバレッジを強いる。このフィードバック(2018年の「ボルマゲドン」や様々な暗号資産のデレバレッジ・カスケードでよく文書化されている)は、多くのプレイヤーがこのルールを使うときにこそルールがうまく機能しなくなることを意味する。これはこのルールを放棄する理由にはならないが、レバレッジに上限を設け、ボラティリティ急騰時の約定が平穏な市場の約定と似ているとは想定しない理由にはなる。
- 予測に下限を設けクリップすること。 ゼロあるいはゼロに近い予測ボラティリティは無限大のレバレッジを生む。 には常に妥当な最小値の下限を設け、ポジションに上限を設け、それぞれがどれくらいの頻度で効いているかを記録せよ——もし上限がほとんどの時間で効いているなら、その資産に対して目標が攻撃的すぎるということである。
まとめ
- ボラティリティ予測は、意思決定を変えるまでは価値を持たない。ボラティリティターゲティング——エクスポージャーを (上限付き)としてサイジングすること——は、予測の価値の最もクリーンなテストである。なぜなら、予測品質が直接、より平坦な実現ボラティリティのプロファイルとより高いシャープにマップされるからである。
- ボラティリティターゲティングはリスク調整後リターンを高め、特にドローダウンを制御する。なぜならボラティリティは予測可能である(クラスター化する)のに対し方向性はそうではなく、シャープレシオはこのルールが自動的にウエイトを下げる高ボラティリティのレジームで低下するからである。これは期待リターンがボラティリティにスケールするという仮定の下でのKellyサイジングである。
- GARCHを、ローリング実現ボラティリティ、EWMA/RiskMetrics(、自由パラメータゼロのIGARCH)、そしてイントラデイの実現分散のプロキシに基づくHAR-RVという強力なベースラインと誠実にベンチマークせよ。EWMAとHARは打ち破りにくい。
- 真のボラティリティは観測できないので、プロキシ(、あるいはそれよりはるかに良い )に対してプロキシのノイズに頑健な損失関数を用いて評価すること。MSEよりもQLIKEを優先せよ——それは過小予測(高くつく誤り)をより強くペナルティし、スケール不変であるため少数の高ボラティリティ日に乗っ取られない。バイアスを診断するにはMincer-Zarnowitzを、ある予測のエッジが本物かノイズかを判断するにはDiebold-Mariano検定を使うこと。
- コストを考慮したウォークフォワード・バックテストにおいて、ボラティリティターゲティングはシャープとドローダウンで固定ノーショナルを確実に上回り、4つの予測手法は互いに近くクラスターする——GARCHがEWMAに勝るエッジは小さく、レジームに依存し、しばしば統計的に有意ではない。平均損失だけでなくDM検定を報告せよ。
- 正直であること。ボラティリティターゲティングはリスクマネジメントであってアルファではない。 それは既に持っている方向性の賭けのリターン分布の形を変えるのであって、無からエッジを生み出すのではない。そしてそれは回転率が高くレバレッジがスケールされるため、通常以上にライブの結果がバックテストに遅れをとる。
参考文献:
- Patton, A. J. (2011). Volatility forecast comparison using imperfect volatility proxies. Journal of Econometrics, 160(1), 246-256. DOI
- Corsi, F. (2009). A simple approximate long-memory model of realized volatility. Journal of Financial Econometrics, 7(2), 174-196. DOI
- Diebold, F. X., & Mariano, R. S. (1995). Comparing predictive accuracy. Journal of Business & Economic Statistics, 13(3), 253-263. DOI
- Mincer, J., & Zarnowitz, V. (1969). The evaluation of economic forecasts. In Economic Forecasts and Expectations, NBER.
- Moreira, A., & Muir, T. (2017). Volatility-managed portfolios. Journal of Finance, 72(4), 1611-1644. DOI
- J.P. Morgan / Reuters (1996). RiskMetrics Technical Document, 4th ed.
- Bollerslev, T. (1986). Generalized autoregressive conditional heteroskedasticity. Journal of Econometrics, 31(3), 307-327. DOI
- Glosten, L. R., Jagannathan, R., & Runkle, D. E. (1993). On the relation between the expected value and the volatility of the nominal excess return on stocks. Journal of Finance, 48(5), 1779-1801. DOI
- Harvey, D., Leybourne, S., & Newbold, P. (1997). Testing the equality of prediction mean squared errors. International Journal of Forecasting, 13(2), 281-291. DOI
MarketMaker.cc Team
クオンツ・リサーチ&戦略