GARCH(1,1)によるクリプトのボラティリティ予測
BTCの日次リターンのチャートを開くと、ランダムウォークの教科書が決して教えてくれなかったことに気づくはずだ。穏やかさと混乱はクラスターになって現れる。6%の下落日が単独で起きることは稀だ。それは4〜8%の値動きが続く1週間の中に埋め込まれており、その後市場は一息つき、1%程度の眠たいセッションが1か月続いた後、次の嵐がやってくる。リターンそのものはほぼ予測不可能に見える——明日が上がるか下がるかを確実に言い当てることはできない——しかし、その大きさは深く予測可能である。今日の乱高下は、明日の乱高下について多くを語る。
トレーダーが手にするほぼすべてのリスクツールは、暗黙のうちにこれが成り立たないと仮定している。Black-Scholesは単一の定数でオプションを価格付けする。静的なValue-at-Riskの数値は、単一のボラティリティ推定値に正規分布の分位点を掛け合わせる。固定3%のストップロスは、完全に横ばいの火曜日とFOMC発表や大手取引所のデペッグの前後の数時間を、あたかも同じリスクを持つかのように扱う。これらはいずれも同じ形で破綻する——時間とともに変化する量を定数に押し込め、その定数が実際には動くことに驚かされるのだ。
この記事は、クリプトのボラティリティモデリングに関する4部構成シリーズの第1部である。基礎を築く内容として、GARCH(1,1)モデル、それがなぜクリプトのリターンにこれほど適合するのか、archライブラリを使って最尤法で誠実に推定する方法、そして条件付き分散予測を直ちに役立つ2つのもの——市場と共に呼吸するポジションサイズとストップ幅——に変換する方法を扱う。第2部では非対称性とファットテールを加え、第3部では多変量に拡張し、第4部で完全なボラティリティ・ターゲティングのバックテストを組み立てる。ここでの応用はあえてシンプルにとどめる。ウォークフォワード検証を経た誠実な戦略は第4部のテーマである。
クリプトリターンのスタイライズド・ファクト
何かをモデル化する前に、私たちが再現しようとしているものを正確に把握しておく価値がある。実証的な金融リターン——株式、FX、そしてとりわけクリプト——は、数十年にわたって文書化されてきた、頑健な統計的規則性の小さな集合を共有している。これらは通常スタイライズド・ファクトと呼ばれ、そのうち3つがこの先の議論すべてを牽引する。
1. ボラティリティ・クラスタリング。 大きな値動きは(符号を問わず)大きな値動きに続きやすく、小さな値動きは小さな値動きに続きやすい。Mandelbrotは1963年に綿花価格でこれに気づいた。形式的には、リターンはほぼ系列無相関である一方、二乗リターン(実現分散の代理変数)は強く、ゆっくりと減衰する正の自己相関を示す。
2. ファットテール(尖度過剰)。 リターンの無条件分布は、ガウス分布よりもはるかに多くの質量が極端な領域に存在する。正規分布の尖度が3であるのに対し、BTCの日次対数リターンは日常的に8〜10を超え、より高頻度のクリプトリターンはさらに悪化しうる。正規モデルではおよそ100万年に一度しか起きないはずのシックスシグマ・デイが、10年間に何度も出現する。
3. リターンには線形自己相関がないが、二乗リターンには強い自己相関がある。 これは、真のボラティリティ過程を単純なトレンドと区別する指紋である。を自身のラグに回帰しても、活用できるものは何も得られない。をそのラグに回帰すると、明確で持続的なシグナルが見つかる。これはまさに分散モデルが捉えるべき構造であり——まさに定数モデルが捨て去ってしまうものである。
これら3つはすべて数行のコードで一目瞭然にできる。特別なデータソースは不要で、本番環境ではccxtを使うべきだが、再現可能なスニペットとしてはyfinanceで十分だ。
import numpy as np
import pandas as pd
import yfinance as yf
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.stats.diagnostic import acorr_ljungbox
from scipy import stats
px = yf.download("BTC-USD", start="2021-01-01", end="2025-12-31")["Close"]
ret = np.log(px / px.shift(1)).dropna() # log returns
ret = ret.rename("btc")
print(f"Observations: {len(ret)}")
print(f"Ann. volatility: {ret.std() * np.sqrt(365):.2%}")
print(f"Skewness: {stats.skew(ret):.2f}")
print(f"Excess kurtosis: {stats.kurtosis(ret):.2f}") # 0 == Gaussian
lb_ret = acorr_ljungbox(ret, lags=[10], return_df=True)
lb_ret2 = acorr_ljungbox(ret ** 2, lags=[10], return_df=True)
print("\nLjung-Box p-value, returns (lag 10):", float(lb_ret["lb_pvalue"].iloc[0]))
print("Ljung-Box p-value, squared (lag 10):", float(lb_ret2["lb_pvalue"].iloc[0]))
典型的な結果(例示であり、実際のウィンドウによって異なる)は次のようになる——尖度過剰は3を大きく上回り、生のリターンに対するLjung-Box検定のp値は「自己相関なし」を棄却できず、二乗リターンに対するp値は実質的にゼロになる。この最後の対比こそがすべてである。日次スケールではリターンの符号に取引可能な材料は何もないが、その分散には大量の構造があり、その構造は予測可能なのだ。
クリプトの24時間365日という性質について一言。株式と異なり、オーバーナイトギャップも週末のクローズもないため、「日」はきれいな24時間バーであり、年率換算係数はではなくである。ボラティリティ・クラスタリングは日中スケールでも見られ、これは時間足でGARCHを実行する場合に重要になる——ファンディングレートの反転や清算カスケードは、日次モデルが平滑化してしまう鋭く集中した分散のバーストを注入する。
ARCHからGARCHへ
問題は明確に定式化された——定数ではなく直近の過去に依存する分散をモデル化することだ。これを適切に行った最初のモデルがEngleのARCH(Autoregressive Conditional Heteroskedasticity、1982年)であり、彼はこれによって2003年にノーベル賞を受賞した。
リターンを条件付き平均とショックの和として書く。
ここでは条件付き分散——時刻までに知られているすべてを所与としたときのの分散——であり、は標準化されたイノベーション(最も単純なケースでは標準正規分布)である。「条件付き」という言葉がすべての働きをしている——無条件では分散は一定かもしれないが、昨日を条件とすると分散は動く。
EngleのARCH()は、今日の分散を直近個の二乗ショックの加重和とする。
ただし分散を正に保つためかつとする。これはクラスタリングを直接捉える——大きなショックがを押し上げ、それが次の大きなショックの確率を高め、分散が高止まりし続ける。問題は実証的な減衰の仕方だ。実際の市場におけるボラティリティの持続性は多数のラグにまたがるため、それをフィットさせるにはARCHモデルは大きな——しばしば8、10、あるいはそれ以上——を必要とし、それは過学習しがちな長く不安定なのベクトルを推定することを意味する。
1986年のBollerslevの洞察は、その持続性のすべてを単一のパラメータで吸収する項を追加することだった。GARCH(1,1)——一般化ARCH——の漸化式は次の通り。
3つのパラメータ、3つの明快な解釈がある。
- — ベースラインあるいは床。分散の長期水準を固定する定数。分散はが支える水準より下には減衰しない。
- — ニュースへの反応。昨日のサプライズに対して分散がどれほど激しく反応するか。大きなは、条件付き分散が跳ねやすくショックに敏感であることを意味する。
- — 持続性あるいは記憶。昨日の分散のうちどれだけが今日に持ち越されるか。大きなは、ボラティリティが滑らかで消えにくいことを意味する——穏やかさは穏やかなまま、嵐は嵐のままである。
その優美さは漸化式の中にある。自体がの項を含んでいたため、これを後ろ向きに展開すると、GARCH(1,1)は過去の二乗ショックに幾何級数的に減衰する重みを持つARCH()であることが分かる。
つまり、単一のが過去のショックに対する無限の指数加重記憶を買い取ってくれる。これが、たった3パラメータのGARCH(1,1)が10パラメータのARCHモデルを日常的に凌駕し、応用ボラティリティモデリングの主力となった理由である。実際、これはRiskMetricsのEWMA分散推定量の近縁種でもある——EWMAは、でを0.94に固定した特殊ケースにあたる。GARCHはデータに、、そして真の平均回帰水準を選ばせることで、これを一般化している。
性質:定常性、長期分散、ハーフライフ
GARCH(1,1)の漸化式にはいくつか導出しておく価値のある性質がある。これらは、モデルを盲目的にフィットさせるのではなく、それについて考察することを可能にしてくれるからだ。
無条件(長期)分散。 過程が共分散定常であり、無条件分散が存在し時間を通じて一定であると仮定する。漸化式の両辺の期待値を取る。であることから、
これがボラティリティの平均回帰先となる水準である。これが存在し、かつ正であるのはのときに限られる。
定常性条件。 その同じ不等式、
がGARCH(1,1)の共分散定常性の条件である。量は分散過程の持続性であり——分散ショックがへと減衰して戻る速さを支配するAR(1)係数である。もしならば、無条件分散は無限大(または未定義)となり、ショックは決して完全には消えない。
平均回帰を明示的に確認できる。分散の偏差を定義する。漸化式に少し代数操作を施す(を代入する)と、期待値において次が得られる。
現在の分散とその長期水準とのギャップは、1ステップごとにの割合で縮小する。これがまさに後で使う複数ステップ予測である。
ボラティリティ・ハーフライフ。 分散ショックが通常水準まで半分回復するのにどれだけかかるか。と置いて解くと、
の場合、ハーフライフは約13.5日、の場合は約34日、の場合は約69日である。この単一の数値は、生のパラメータよりも直感的であることが多い——それは使っているバーの単位で、ボラティリティがどれほど粘着的かを教えてくれる。
クリプトにおける準IGARCH問題。 ここにクリプト特有の癖がある。BTCやETHのリターンにGARCH(1,1)をフィットさせると、ほぼ常にが1に極めて近い値——0.98、0.99、時に0.995といった値——になる。これは準IGARCH(Integrated GARCH)の状態である。これには現実的な帰結がある。
- ハーフライフが(数週間から数か月へと)膨大になり、モデルはボラティリティを非常に持続的で、ほとんど平均回帰しないものとして扱う。
- の推定値が極めて敏感になる——が0.99から0.995へのわずかな変化で、含意される長期分散は2倍になる。この体制下では、信頼区間なしに長期ボラティリティの点推定を決して信用してはならない。
- 複数ステップ予測は非常にゆっくりとしか平均回帰しないため、数週間未満の実務的なホライズンでは、GARCHはほぼ「分散のランダムウォーク」(EWMAが仮定するもの)のように振る舞う。
準積分性が本物なのか、それとも構造変化(ボラティリティ水準の恒久的なシフトを、モデルが1つの長い持続的エピソードとして読み取っているだけ)のアーティファクトなのかは、現実的な論争点である。これは、全履歴に対して一度だけフィットさせるのではなく、ローリングウィンドウで再フィットすべきもう一つの理由であり、この点は落とし穴のセクションで再び取り上げる。レジーム構造そのものは、明示的なスイッチングモデルの方がうまく扱える——隠れマルコフモデルによるレジーム検出を参照。これはGARCHの代替ではなく補完である。
最尤法による推定
GARCHのパラメータは最尤法で推定される。ロジックは直接的だ——が与えられれば、漸化式は条件付き分散の完全な経路を生成し、イノベーションについて仮定した分布のもとで、観測されたリターンがどれだけ「ありそうか」を書き下すことができる。そして、その尤度を最大化するを選ぶ。
ガウス的なイノベーションを仮定すると、となる。1つの観測値の条件付き密度は
モデルが条件付きで書かれているため、結合尤度は1ステップ先密度の積に分解され、対数尤度は単純な和になる。
注目すべき構造的な事実が2つある。第一に、はペナルティ項(——モデルは高い分散を主張することで罰せられる)と標準化残差項(——モデルはサプライズによって罰せられる)の両方に現れる。最適解はこの2つのバランスを取り、それが分散を追跡させる原動力となる。第二に、漸化式には種となるが必要であり、通常はリターンの標本分散が選ばれ、数千の観測値があれば種の選び方はほとんど問題にならない。
最大化問題に閉じた形の解はないため、数値的に最適化する(archは解析的または数値的な勾配を用いた準ニュートン法を使う)。尤度曲面はGARCH(1,1)については概ね良好な振る舞いをするが、実務上は2点で問題が生じる——正値制約()と、のときの境界付近での挙動で、そこではオプティマイザの進みが遅くなることがある。両方とも良いライブラリを使えば処理してくれる——そして実際に使うべきだ。GARCHの最尤推定を手作りするのは良い学習演習だが、本番用途としては不出来な選択である。
archライブラリ
Kevin Sheppardによるarchパッケージは、Pythonにおける標準的なツールである。フィット全体はたった4行で書ける。
from arch import arch_model
r = ret * 100.0
model = arch_model(r, mean="Constant", vol="Garch", p=1, q=1, dist="normal")
res = model.fit(disp="off")
print(res.summary())
引数名について一言——よくある混乱の原因なので。archでは、pはラグ付き分散の数(項、GARCHの次数)で、qはラグ付き二乗残差の数(項、ARCHの次数)である。したがってp=1, q=1は、私たちが導出したGARCH(1,1)そのものである(Bollerslevのオリジナルの記法ではGARCH()と書き、そこではがARCHの次数を表す——2つの記法は転置の関係にある。記憶ではなくライブラリ自身のドキュメントを信頼すること)。
サマリーを読むと、係数テーブルはおおむね次のようになる(BTC日次リターンの例示的な値であり、実際の実験結果ではない)。
Volatility Model
==========================================================
coef std err t P>|t|
----------------------------------------------------------
omega 0.4821 0.201 2.40 0.016
alpha[1] 0.0912 0.021 4.34 0.000
beta[1] 0.8994 0.024 37.5 0.000
==========================================================
読み方は次の通り。
alpha[1] + beta[1]= 0.0912 + 0.8994 = 0.9906。持続性は1をわずかに下回る——まさに警告した通りの準IGARCH体制である。ハーフライフは日。omega= 0.4821なので、長期分散はパーセント二乗単位で、すなわち長期日次ボラティリティは、年率換算するとおよそとなる。これはBTCとしてもっともらしい数値である。alphaとbetaはいずれも強く有意である。alphaがbetaに対して小さいのは典型的で、クリプトの分散は主に持続性(記憶)で構成されており、新たなショックへの反応は控えめだが確かに存在する。
×100スケーリングの落とし穴
これはarchから意味不明な結果を得てしまう最も一般的な原因なので、独立した小見出しに値する。オプティマイザは、扱う数値がから程度のときに最もよく機能する。日次対数リターンはであるため、その二乗はとなり、は前後——数値勾配が精度を失い、フィットが静かに収束に失敗したりでたらめな標準誤差を返したりする範囲——に落ち込んでしまう。
対策は、上記のようにリターンを100倍にスケーリングして(すなわちパーセント単位で)フィットさせることだ。忘れるとarchはDataScaleWarningさえ出してくれる。モデルから読み出すものはすべてパーセントまたはパーセント二乗単位になるため、一貫してスケールを戻す必要がある。
sigma_pct = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0])
sigma_decimal = sigma_pct / 100.0
print(f"1-day-ahead conditional vol: {sigma_decimal:.2%}")
スケールされた量とスケールされていない量を混在させること——たとえば、小数を期待するポジションサイジングの式にパーセント単位のボラティリティを与えてしまうこと——は、まさに100倍の誤差を生み出す。コードは問題なく動くため、見逃しやすい。1つの規約を選び(私はフィット部分の外ではすべて小数で扱い、archとの境界でのみスケーリングする)、決してそれを越えないこと。
条件付き分散の予測
フィットしたモデルは、予測ができて初めて役に立つ。GARCHは任意のホライズンできれいな解析的予測を与えてくれる。
1ステップ先。 時刻(サンプルの終端)では、とが既知なので、次の分散は決定的に定まる。
期待値を取る必要はない——右辺のすべてが観測済みだからだ。
複数ステップ先。 については、その間のショックがまだ分からないため、条件付き期待値を取る。(であるため)を用いると、漸化式は予測された分散に関する単純なAR(1)に崩れる。
これを1ステップ先予測から反復すると閉じた形が得られる。これは先に導出した平均回帰の結果を明示的に書き出したものである。
これは注意深く読む価値がある。あらゆるGARCH予測の幾何構造そのものだからだ。分散のタームストラクチャーは今日の条件付き分散から始まり、長期水準へと幾何級数的に減衰する。今日が平均より穏やかであれば、予測曲線はに向かって上昇する。今日が危機的であれば、それに向かって下降する。その減衰の速さは完全にによって決まる——そして準IGARCHのクリプト体制では、であるため、減衰は非常に遅く、2週間未満のホライズンでは予測はほとんど今日の水準から動かない。これは内面化しておく価値がある——短い保有期間では、クリプトのGARCH予測は本質的に「明日は今日に似ているが、非常にゆっくりとしか平均回帰しない」ということだ。
保有ホライズンへの集約。 トレーダーが1日先だけの分散を気にすることは稀である。もしポジションを日間保有し、リターンが条件付き無相関である(冒頭のスタイライズド・ファクト)とすれば、累積日リターンの分散は、1日先予測分散の和になる。
これが実際にサイジングの対象とすべき数値——保有期間にわたるP&Lのボラティリティである。これは決して素朴なのスケーリングではないことに注意——それは分散が一定である場合にのみ正しい。今日の分散がより高いとき、平均回帰する予測は真の日ボラティリティを平方根則より低くする。今日が穏やかなときは高くする。ここを正しくすることが、タームストラクチャーを尊重するストップとそうでないストップの違いを生む。
コードで示すと以下の通り。
H = 10
fc = res.forecast(horizon=H, reindex=False)
var_path_pct2 = fc.variance.iloc[-1].values # [E_T sigma^2_{T+1}, ..., T+H]
var_path = var_path_pct2 / (100.0 ** 2) # back to decimal variance
daily_vol = np.sqrt(var_path)
print("Forecast daily vol path:", np.round(daily_vol * 100, 2), "%")
H_day_var = var_path.sum()
H_day_vol = np.sqrt(H_day_var)
print(f"{H}-day holding-period vol: {H_day_vol:.2%}")
naive = np.sqrt(var_path[0] * H)
print(f"Naive sqrt(H) * sigma_1: {naive:.2%}")
より長いホライズンについては、GARCHはシミュレーション予測(method="simulation")もサポートしており、これはイノベーション分布を前方に伝播させ、分散だけでなく予測分布全体を与えてくれる——イノベーションが非ガウス的である場合(第2部でStudent-tや歪んだ分布に移行すればそうなる)に有用である。上記の分散に関して線形な量については、解析的な経路が厳密かつ無コストである。
診断:モデルは実際に機能したか
モデルをフィットさせることと、それを検証することは同じではない。GARCHの要点は、条件付き分散不均一性——ボラティリティ・クラスタリング——を吸収し、残るものが(ほぼ)i.i.d.になるようにすることだ。そのため正しいチェックは、標準化残差
を見て、「クラスタリングは消えたか」を問うことである。モデルが分散のダイナミクスを捉えていれば、は単位分散を持ち、決定的に重要な点として、その二乗には残存する自己相関が見られないはずだ。3つの検定を行う。
1. 標準化残差に対するLjung-Box検定。 の水準に線形自己相関が残っていないかを確認する(これは実質的に分散モデルではなく平均モデルを検定している)。棄却されないべきである。
2. 標準化残差の二乗に対するLjung-Box検定。 これが重要な検定である。もしに依然として有意な自己相関があれば、分散モデルはクラスタリングを取り除けなかったことになる——GARCH(1,1)が捉えられなかった構造があり、より高次のモデル、非対称なバリアント、あるいは別のイノベーション分布が必要かもしれない。棄却されないべきである。
3. ARCH-LM検定(Engleのラグランジュ乗数検定)。 をそれ自身のラグに回帰し、同時有意性を検定する。これは本質的に検定2の正式版であり、「ARCH効果は取り除かれずに残っているか」を直接問う。有意でない結果は、条件付き分散不均一性がモデルによって除去されたことを裏付ける。
from statsmodels.stats.diagnostic import acorr_ljungbox
from arch.unitroot.cointegration import engle_granger # (unrelated; shown for import clarity)
z = res.std_resid.dropna() # standardized residuals
z2 = z ** 2
lb_z = acorr_ljungbox(z, lags=[10, 20], return_df=True)
lb_z2 = acorr_ljungbox(z2, lags=[10, 20], return_df=True)
print("Ljung-Box on standardized residuals:\n", lb_z, "\n")
print("Ljung-Box on SQUARED standardized residuals:\n", lb_z2, "\n")
lm = res.arch_lm_test(lags=10, standardized=True)
print(lm)
print(f"\nStd-resid excess kurtosis: {stats.kurtosis(z):.2f}")
良好な出力とは次のようなものだ——に対するLjung-Boxのp値が(生の二乗リターンに対するほぼゼロの値から)0.05を快適に上回る水準にジャンプし、ARCH-LM検定は棄却に失敗する。これが、モデルが第二モーメントについてその役割を果たしたという証拠である。
不完全な出力とは——そしてクリプトに対する素朴なガウスGARCH(1,1)で予期すべきものは——クラスタリング検定は通過するものの、標準化残差の尖度が依然として高い(0ではなく4〜6程度)というケースである。GARCHはクラスタリングを取り除くが、単一のファットテール無条件分布が残る。なぜならガウス的なイノベーションはテールを再現できないからだ。この残存するファットテール性は、ここで修正すべきバグではない——それは第2部、クリプトにおける非対称GARCHとレバレッジ効果の動機そのものであり、そこではStudent-tおよび歪みStudent-tのイノベーションとGJR/EGARCHの非対称項が、まさにこの問題に対処する。
応用:ボラティリティ調整サイジングとストップ
これで明日の(そして今後日間の)ボラティリティ予測が手に入った。これをどう使うか。最もシンプルで価値の高い2つの用途は、ポジションサイジングとストップ設定である。ここでは両方とも意図的に基本的なレベルにとどめる——実用上のあらゆる仕組みを備えた完全なボラティリティ・ターゲティング戦略は第4部のテーマである。
ボラティリティ・ターゲット型ポジションサイジング
考え方は、ノーショナルが一定のポジションではなく、リスク寄与が時間を通じてほぼ一定であるポジションを保有することだ。常に同じドル建てサイズを展開すれば、高ボラティリティ体制ではリスクが膨れ上がり、穏やかな体制では萎んでしまう——望ましい方向とは正反対だ。ボラティリティ・ターゲティングはこれを逆転させる——P&Lの固定目標ボラティリティを目指し、予測にサイズを決めさせる。
目標年率ボラティリティ(例えば20%)と予測年率ボラティリティに対して、ポジションウェイトは
予測ボラティリティが高いときは縮小し、低いときは拡大する。それがメカニズムのすべてである。は予測——のリターンが実現する前の時刻で既知である——なので、タイミングについて規律さえ保てば(落とし穴のセクションで詳しく述べる)先読みは発生しない。
def vol_target_weight(sigma_forecast_annual, sigma_target_annual=0.20,
w_max=3.0):
"""Volatility-scaled position weight. Inputs/outputs in decimals.
w_max caps leverage so a tiny forecast vol can't demand insane size."""
w = sigma_target_annual / sigma_forecast_annual
return float(np.clip(w, 0.0, w_max))
sigma_1d = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0]) / 100.0
sigma_ann = sigma_1d * np.sqrt(365)
w = vol_target_weight(sigma_ann, sigma_target_annual=0.20)
print(f"Forecast annual vol: {sigma_ann:.1%} -> position weight: {w:.2f}x")
これは、適切な資本配分ルールの近縁種である。ボラティリティ・ターゲティングは「リスクはボラティリティに応じてどれだけスケールすべきか」に答え、Kelly基準は「リスクはエッジに応じてどれだけスケールすべきか」に答える——そしてこの2つは完全なサイジング体系の中で掛け合わされる。サイズエッジ/分散、という具合だ。Kellyの分散項は、まさに今計算したGARCH予測そのものであり、これがライブのボラティリティモデルが静的な過去推定値よりも実質的にKellyサイジングを鋭くする理由である。もしエッジの推定自体が定量化された不確実性を伴うなら、コンフォーマル予測は分布に依存しない方法でサイズを拡大・縮小させることができ、ボラティリティ・ターゲティングとも綺麗に組み合わさる。
キャップw_maxは任意ではない。準IGARCH体制では、静かな期間が予測ボラティリティをかなり低く押し下げることがあり、は、紙の上では問題なく見えて穏やかさが崩れたときには破滅的なレバレッジを要求する——そしてボラティリティ・クラスタリングの性質上、それはしばしば突然、いずれ必ず崩れる。レバレッジにキャップをかけることは、予測が条件付き平均であって保証ではないこと、そして間違えたときの損益が非対称であることを認める、粗いが効果的な手段である。その非対称性——爆発した口座は対称的な勝ちでは取り戻せない——はまさに損失と利益の非対称性であり、分散のみに基づくルールが示唆するよりも体系的に保守的であるべき理由である。
ボラティリティ調整ストップ
固定パーセンテージのストップは、固定ポジションサイズと同じ病を抱えている——3%のストップは穏やかな市場では過敏なトリガーであり、激しい市場では誤差の範囲でしかない。それは高ボラティリティ体制において通常のノイズであなたを良いポジションから振り落とし、移行期にはあまりに多くを返してしまう。対策は、ストップの距離を予測ボラティリティの単位で設定することだ。
ここでは期待される保有ホライズンにわたる予測ボラティリティ(予測のセクションで得た集約された量)であり、は倍数——通常1.5から3——で、ストップが通常の変動の外側かつ本物の逆行動きの内側に位置するように選ばれる。
def vol_scaled_stop(entry_price, side, sigma_H, k=2.0):
"""
entry_price : fill price
side : +1 long, -1 short
sigma_H : forecast volatility over the holding horizon (decimal)
k : stop width in vol units
Returns the stop price.
"""
stop_frac = k * sigma_H
return entry_price * (1.0 - side * stop_frac)
var_path = res.forecast(horizon=10, reindex=False).variance.iloc[-1].values / (100.0 ** 2)
sigma_H = np.sqrt(var_path.sum())
entry = float(px.iloc[-1])
stop = vol_scaled_stop(entry, side=+1, sigma_H=sigma_H, k=2.0)
print(f"Entry {entry:,.0f} | 10-day vol {sigma_H:.2%} | 2-sigma stop {stop:,.0f}")
は、平坦な過去の数値ではなく平均回帰するタームストラクチャー予測を使用するため、ストップは荒れた体制に向かうと自動的に広がり、ボラティリティが収まると縮まる——タームストラクチャーがあなたの代わりに調整してくれる。これはサイズとストップの両方に同じ予測が流れ込むことを意味し、それは特徴である——高ボラティリティ体制では、同時により少なく保有し、ポジションにより多くの余地を与えることになり、この2つの効果が複合してテールリスクを実質的に低下させる。サイジングとストップは、1つのボラティリティ観に基づく2つの投影であり、独立した2つのノブではない。
第1部での応用としてはここまでにとどめる。実際の戦略は、絶え間ないリバランスに伴う取引コスト、予測が計算されるタイミングと取引が執行されるタイミングの関係、ターンオーバー管理、そして何よりも誠実なアウトオブサンプル評価を扱わなければならない。それらすべては第4部:ボラティリティ・ターゲティングGARCH戦略で、私たちが全体を組み立ててウォークフォワードでテストする内容である。
落とし穴
GARCHはフィットさせるのは容易だが、それゆえに自分自身を欺きやすい。失敗のパターンは一貫している。
リターンのスケーリング。 上で扱った通りだが、これが最大のバグなので繰り返しておく価値がある——archは×100したリターンでフィットさせ、すべての出力を(分散はで、ボラティリティはで)スケールし直すこと。ここでの静かな100倍のエラーは、以降のすべてのサイジングとストップの計算を毒する。
フィットにおける先読み。 最も陰湿な失敗だ。モデルを全履歴でフィットさせ、その同じ履歴について「予測」を計算すると、すべての予測はひそかに未来を見てしまっている——パラメータは予測日より後のデータを使って推定されているからだ。インサンプルのフィットは素晴らしく見える一方、ライブでのパフォーマンスはそれとまったく似ても似つかないものになる。バックテストされたすべての予測は、その時点で利用可能なデータのみでフィットされたモデルから得られなければならない——拡張ウィンドウあるいはローリングウィンドウで再フィットし、1ステップ予測し、前に進む。これは交渉の余地がなく、ウォークフォワード最適化というテーマそのものである。インサンプルのGARCHと適切にウォークフォワードで検証されたGARCHとのギャップは、デモとライブ市場との接触に耐えるシステムとのギャップである——バックテストとライブのパリティも参照。
予測のタイミング。 関連するが別の問題である。日の予測は、日の(あるいはバーがクローズする時点の)クローズで利用可能な情報から計算されなければならず、ポジションは実際に取得可能な価格で執行可能でなければならない。日のクローズを使って予測を計算し、それを日のオープンで「取引」するのは、あらゆる結果を静かに水増しする先読みである。
高次数の過剰適合。 GARCH(1,1)でほぼ常に十分である。インサンプルの対数尤度をわずかに押し上げるからといってGARCH(2,2)やGARCH(3,1)をフィットさせたいという誘惑は、たいていノイズへの過剰適合である。追加のパラメータがアウトオブサンプル予測を改善することは稀で、境界付近ではオプティマイザを不安定にすることも多い。倹約的なモデルを選好し、もし次数を比較するなら、インサンプルAICではなくウォークフォワード分割のアウトオブサンプル予測損失で比較すること。残差診断が依然として問題を示すとき、修正は通常はより高次のモデルではなく、より良いイノベーション分布あるいは非対称項(第2部)である。
構造変化を持続性と誤読する。 すでに述べた通り、ボラティリティ水準の恒久的なシフト(新しい市場レジーム、市場マイクロストラクチャーの変化)は、GARCHによって見かけ上高い持続性として吸収され、を1に押し上げることがある。ウィンドウ間で長期ボラティリティの推定値が不安定に見えるなら、準IGARCHの点推定を信頼するのではなく、構造変化を疑うべきだ。ローリングでの再フィットと、適切な場合には明示的なレジームモデルがこれを防いでくれる。
ボラティリティ予測をリターン予測と取り違える。 GARCHは値動きの方向ではなく大きさを予測する。明日のスイングがどれくらい大きくなりそうかを教えてくれるが、どちらの方向かは教えてくれない。だからこそ、その本来の居場所はシグナル生成ではなく、サイジング、ストップ、VaRといったリスク管理なのだ。良好な分散予測をエッジと取り違えてはならない。
この先の展開
GARCH(1,1)は基礎であり、意図的に不完全である。このシリーズはそこから3つの方向に発展する。
- 非対称性とファットテール — 実際のクリプトのボラティリティは上昇よりも下落に強く反応する(レバレッジ効果)、そしてガウス的なイノベーションはテールを再現できない。GJR-GARCH、EGARCH、Student-t/歪みStudent-tのイノベーションは第2部で扱う。
- 多変量ボラティリティ — クリプト資産間の相関それ自体が時間とともに変化し、暴落時に急上昇する。共分散行列全体を動的にモデル化するのは第3部:DCC-GARCHであり、これは共分散が動的になった段階でMarkowitz平均分散やCVaRベースの配分に直接つながる。
- 完全な戦略 — サイジング、ストップ、コスト、ターンオーバー、そして誠実なウォークフォワード評価は第4部で統合される。
そして、GARCHの周辺分布が同時リスクにどうつながるか——ここでの単変量条件付き分散モデルは、まさにポートフォリオVaR/CVaRのためのGARCH-EVT-コピュラパイプラインの第一段階である。資産ごとのGARCHフィットから標準化残差を得たら、それを変換してコピュラで結びつける——周辺分布はGARCH、依存構造はコピュラが担う。この構成は、テール依存性とEVTのテール処理も含めて、クリプトの同時リスクのためのコピュラモデルで詳しく扱われている。本稿はその下に位置する単変量エンジンである。
まとめ
- クリプトのリターンはボラティリティ・クラスタリング、ファットテール、そしてリターンの自己相関はないが二乗リターンには強い自己相関があるという性質を示す。定数ボラティリティを仮定するあらゆるツール——単一のを使うBlack-Scholes、静的なVaR、固定パーセンテージのストップ——は、これらの事実に対して定式化を誤っている。
- GARCH(1,1)、は、3つのパラメータ——ベースライン、ショック反応、持続性——で時間変動する条件付き分散をモデル化する。これは幾何級数的に減衰する記憶を持つARCH()であり、それが高次のARCHを凌駕する理由である。
- 定常性にはが必要であり、長期分散は、持続性は、ボラティリティのハーフライフはである。クリプトは準IGARCH体制()に位置する——非常に持続的で、平均回帰が遅く、長期分散推定は脆い。
- 最尤法で推定する。 ガウス対数尤度は1ステップ密度の和であり、
arch_model(r*100, vol="Garch", p=1, q=1)でフィットさせる。×100のスケーリングを忘れず、すべての出力を一貫してスケールし直すこと。 - 予測はレートで長期分散に向かって幾何級数的に平均回帰する。日次分散予測を集約して保有ホライズンのボラティリティを得る——素朴な則ではなく。
- 標準化残差の二乗に対するLjung-Box検定とARCH-LM検定で検証する。これらに通ることはクラスタリングがモデル化によって取り除かれたことを裏付ける。残存するファットテールは第2部の動機となる。
- 応用としては、ボラティリティ・ターゲット型サイジング(、上限付き)とボラティリティ調整ストップ()がある。1つの予測が両方を駆動するため、高ボラティリティ体制では小さいサイズと広いストップが同時に得られる。
- 重要な落とし穴——リターンのスケーリング、フィットにおける先読み(過去のデータのみでフィットし、常にウォークフォワードで)、予測のタイミング、高次数への過剰適合、そして分散予測を方向予測と決して取り違えないこと。
参考文献:
- Engle, R. F. (1982). Autoregressive Conditional Heteroscedasticity with Estimates of the Variance of United Kingdom Inflation. Econometrica, 50(4), 987-1007. DOI
- Bollerslev, T. (1986). Generalized Autoregressive Conditional Heteroskedasticity. Journal of Econometrics, 31(3), 307-327. DOI
- Mandelbrot, B. (1963). The Variation of Certain Speculative Prices. The Journal of Business, 36(4), 394-419. DOI
- Nelson, D. B. (1991). Conditional Heteroskedasticity in Asset Returns: A New Approach. Econometrica, 59(2), 347-370. DOI
- Katsiampa, P. (2017). Volatility estimation for Bitcoin: A comparison of GARCH models. Economics Letters, 158, 3-6. DOI
- Chu, J., Chan, S., Nadarajah, S., & Osterrieder, J. (2017). GARCH Modelling of Cryptocurrencies. Journal of Risk and Financial Management, 10(4), 17. DOI
- Sheppard, K. (2023). arch: Autoregressive Conditional Heteroskedasticity (ARCH) and other tools for financial econometrics in Python. GitHub.
MarketMaker.cc Team
クオンツ・リサーチ&戦略