12 ETF 比較:6 手法の ES 変動比 0.671–0.768。Gaussian の同時下落は 0.3180、訓練参照は 0.4211。
図:固定した訓練履歴でシナリオを増やすと標本変動は減るが、Gaussian の同時下落のずれは残る。OOS は別の単期 VaR 3 手法。

モンテカルロ法でリスクを試算していると、結果がぶれたときに「シナリオ数を倍にしよう」と考えがちです。けれど、追加した756件のシナリオは何を増やしたのでしょうか。数値のばらつきが小さくなることと、現実の損失の構造を再現できることは別の話です。

今回は、12本のETFの実際の過去価格、6種類のシナリオ生成法、対応させた50個の乱数シードを使って確かめました。生成件数を756から1,512に増やすと、一部の推定値は確かに安定しました。一方、Gaussianモデルの同時下落率は0.3180のまま。固定した学習データでの参照値は0.4211です。

ここでいう「同時下落率」は、SPYが大きく下がった日に、残りのETFのうち何割が一緒に大きく下がるかという指標です。各ETFにとっての「大幅下落」は学習期間の下位5%という閾値で事前に固定し、生成後に都合よく引き直しません。

私がシナリオ生成器を見るときは、三つの問いを分けます。抽選を増やせば結果のばらつきは減るか。同じ日に複数の資産が下がる傾向や、悪い日が続く構造は残るか。そして、標本外のリスク閾値は校正できているか。 最初の二つは固定した学習期間での比較です。三つ目は別の、より限定的な1日VaRの検証であり、6生成法の標本外ランキングではありません。

12本のETFと固定した756日間の学習データ

価格はBoris Marjanovicの Huge Stock Market Dataset から取得しました。データ提供元はCC0と明記し、配当・株式分割を調整した価格であると説明しています。株式、国債、信用債、金、商品、米ドル、不動産にまたがる12本を選びました。SPYだけを見ても、資産をまたぐ同時下落はわかりません。全銘柄コードは末尾に記します。

元の12ファイルの調整済みCloseを、共通して記録がある日付だけで結合し、欠損日は補完していません。このKaggleデータにおけるHYGの最初の記録が2007-04-11なので、共通期間は2007-04-11から2017-11-10までの価格日2,668件です。そこから日次単純リターンを計算し、12本のETFを毎日等ウェイトで平均します。このポートフォリオの定義は変更しません。

シナリオ生成には、2008年の市場ショックを含む最初の756日次リターンだけを使います。すべての方法を同じ過去データで比較したいからです。Student-tの自由度も5に固定し、後から標本外の成績を見て調整していません。

手法リターン・シナリオの生成方法
Gaussian学習期間の平均と共分散から多変量正規リターンを生成
Student-t df=5自由度5の多変量t分布。共分散は学習期間と一致させる
IID Bootstrap学習期間のETFリターン1日分を行単位で独立に復元抽出
Moving-block 5 / 20 / 60連続する5日・20日・60日のリターンの塊を抽出

まず防ぎたいのは、二つの試行で乱数まで変えてしまい、偶然の差をシナリオ数の効果だと思い込むことです。各手法につき乱数シードを1000–1049の50個に固定しました。同じシードでまず1,512件を生成し、その先頭756件を少数シナリオ側に使います。つまり、756日間の同じ過去情報を繰り返し使っているだけで、実市場の観測日が新たに増えたわけではありません。

Student-tにも落とし穴があります。「裾が厚い分布」に置き換えただけで分散まで増やしてしまうと、公平な比較になりません。実装では学習共分散のCholesky因子で相関のある正規ベクトルを作り、√(3/χ²₅)(χ²₅は自由度5のカイ二乗変数)を掛けます。これはt分布のスケール行列を学習共分散の3/5とすることと同じです。この調整を外すと、生成した共分散は意図した値の5/3倍になります。

シナリオを倍にして、Monte Carloのばらつきはどれだけ減ったか

最初は1日ES(5%)、つまり収益が下位5%に入った場合の平均リターンです。各シードでESを一つ計算し、50個のESについてシード間標準偏差を求めました。そしてN=1,512とN=756で比較します。これは抽出によるばらつきを測ったもので、未来市場での精度を測った数字ではありません。

生成手法シード間ES標準偏差の比:1,512 / 756
Gaussian0.718
Student-t df=50.671
IID Bootstrap0.746
Moving-block 50.768
Moving-block 200.681
Moving-block 600.734

6手法の比は0.671–0.768で、すべて1未満です。この50シードでは、シナリオ増加後にESの分布が狭まりました。1/√2≈0.707は標本数を倍にしたときの直感的な目安ですが、裾の統計量であるESが厳密に同じ速さで収束する必要はありません。

同時下落率は別の結果になりました。SPYが学習期間に固定した下位5%閾値を下回った生成日に限り、残り11本のETFがそれぞれの固定閾値を下回った割合を計算し、SPYの該当日について平均します。Gaussianの学習参照値からの誤差は、N=756で約**−0.1036**、N=1,512でも約**−0.1031**。ほとんど変化していません。

抽出数を増やせば推定値は安定しても、生成モデルが表現できていない同時下落が自然に補われるわけではありません。

IID Bootstrapは同日の共倒れを覚えていても、荒れ相場の連続性を忘れる

同じ日に下がる割合が似ていても、悪い日が連続するかどうかは別です。以下の指標はすべて12本のETFを等ウェイトにした日次ポートフォリオ・リターンから計算します。絶対リターンの1期自己相関 ACF(1) は大きな値動きの連続性を粗く見る指標。60日ESと最大ドローダウンは、さらに長い経路の裾を見ます。

ここでの計算上の注意点は、連日の損益率を単純に足して60日損益としてはいけないことです。生成リターン列を1日ずつずらして60日間の窓を作り、各窓の累積リターンを∏(1+rₜ)-1で複利計算します。下位5%の窓の平均が60日ESです。最大ドローダウンは各窓の複利資産曲線から別々に求め、その5%分位点を使います。

次の表はN=1,512での50シード平均を、同じ固定学習データの参照値と比べたものです。

シナリオ下落時の同時下落率絶対リターン ACF(1)60日 ES(5%)60日最大ドローダウンの5%分位
学習データ参照0.42110.3000-21.01%-25.45%
Gaussian0.3180-0.0011-15.77%-16.53%
Student-t df=50.3549-0.0005-15.97%-17.00%
IID Bootstrap0.42650.0059-15.15%-16.26%
Block 50.42090.2408-14.45%-15.75%
Block 200.42270.2842-16.54%-18.56%
Block 600.41130.2684-17.49%-20.48%

すべての数字を追う必要はありません。IID BootstrapはETF12本の同じ日付の行全体を抽出するので、同時下落率0.4265は学習参照の0.4211に近くなります。ところが日付同士は独立に抽出されるため、ACF(1)は0.0059しか残りません。参照値は0.3000です。

要するに、どの資産が同じ日に下がったかは覚えているのに、大きな値動きが何日も続くかはほぼ忘れてしまうのです。

Block 20のACF(1)は0.2842まで戻ります。Block 60の60日ESはIIDの**−15.15%から−17.49%へと深くなりますが、学習参照の−21.01%**とはまだ差があります。ブロックが長いほどよいわけでもなく、Block 5の60日ESはIIDより浅くなっています。

なぜ窓を重ねるのか。重なりを禁止すると、756日の学習期間から取れる60日の区間は約12本、生成した1,512日からでも約25本だけです。独立した十数区間から下位5%を推定しても私は信用しにくい。1日刻みでずらすと学習側は697窓、生成側は1,453窓になります。ただし大量に重複するので、それだけ多くの独立リスク事象が得られたわけではありません。学習期間には2008年も含まれ、−21.01%はその期間の経験的な参照値であり、精密な真の目標ではありません。

Moving-blockの継ぎ目と、履歴の端にある日付の重み

Block法のACFが上がったとき、私はまず「日付の順序を保つからだ」と説明したくなりました。しかし抽出器を調べ直すと、別の仕組みが混ざっていました。

一つ目はブロックの継ぎ目です。ブロック内部の日付は元の履歴で連続していますが、次のブロックには離れた日付が来ることがあります。長さLなら隣接ペアの約1/Lが継ぎ目を跨ぎます。独立なブロック連結という単純化では、ラグ1の相関はおおむね1−1/Lの規模で弱まります。参照ACF0.3000から見積もるとL=5で約0.240、L=20で約0.285。観測値0.2408と0.2842に近いですが、L=60ではきれいに当てはまりません。これは理解の助けになる近似であり、実測の代わりではありません。

二つ目は学習期間の両端にある日付の抽出頻度が低いことです。通常の非循環Moving-blockで許される開始位置は0から756−L。最初と最後の日付は内部の日付ほど選ばれず、位置による重みの差は最大L倍になります。これが今回の数字をどれほど変えるのか、始点を均等にして末尾から先頭に回り込めるCircular Blockを使い、同じ50シードで追加比較しました。

ブロック長ACF(1):通常 / 循環60日ES:通常 / 循環
50.2408 / 0.2411-14.45% / -14.35%
200.2842 / 0.2884-16.54% / -16.37%
600.2684 / 0.2792-17.49% / -17.23%

実際の差は大きくありません。Block 20のACFは0.2842→0.2884、60日ESは**−16.54%→−16.37%**でした。端の抽出確率が違うことは事実でも、今回のBlockとIIDの主な差をそれだけで説明することはできません。Circularには、歴史の最後と最初を人工的につなぐ副作用もあります。名前に「修正」が入っているからといって、必ず優れるわけではありません。

追加検証でわかったのは、ブロックの継ぎ目が切る時間相関と、履歴端のサンプリング重みを別々に考える必要があることでした。Circular Block Resamplingの方法資料も参照できます。

1日VaR:3つの単期手法は全標本外期間で校正を通過しなかった

ここからは別の、もっと狭い問いです。実際の翌日が来たとき、予測した左裾の閾値は合っているか。 前述の6生成法の標本外順位ではありません。検証するのは過去の経験分位点、Gaussian、自由度5のStudent-tという3つの1日VaR手法だけです。こちらは目標日の直前756日の実際の等ウェイト・ポートフォリオリターンで毎回閾値を推定します。前半の固定学習窓は使い続けません。

Student-tの5%分位も学習分散に合わせています。「VaR超過」とは、翌日の実現リターンが予測された下位5%閾値を下回ることです。重複しない日次の標本外目標は1,911日あります。

1日95% VaR手法超過回数超過率Kupiec p値Christoffersen独立性 p値
過去の経験分位点522.72%6.16e-70.0145
Gaussian512.67%3.16e-70.0122
Student-t df=5623.24%1.75e-40.0171

名目5%なら、1,911日での平均超過期待回数は95.55回です。実際はどの手法も51–62回と少なめでした。しかし超過回数が少ないことは、校正が通った証拠ではありません。Kupiecは超過頻度、Christoffersenは超過の独立性を検定します。表のp値はいずれも小さくなっています。

結論はあくまで1,911日全体についてです。ローリング学習窓は初期に2008年のショックを含みますが、時間とともに外れます。市場局面を分けた検定は今回は行っていません。連続2日の超過回数も3手法で5 / 5 / 6にすぎず、独立性検定はこの事象の少なさも踏まえて読みたいところです。

Block 60の60日ESを無理にこの表に入れることもしませんでした。60日先の損失目標を毎日ずらして作ると互いに重複し、その依存を処理できる別のバックテスト設計が必要です。納得できる設計は今回完成していないため、1日用のKupiec/Christoffersenを使い回して「検証済み」とは言いません。

Student-tについても逆説的な点があります。「裾が厚いからVaRはいつも保守的」とは言い切れません。自由度5、分散一致という条件では、標準化した左側5%分位点は約**−1.56085で、正規分布の−1.64485**よりゼロに近いのです。分布の名前だけで各信頼水準の閾値の向きを決めるのは危険です。

次の計算予算をどこに使うか

今度「結果が安定しないからシナリオを倍にしよう」と言われたら、すぐに計算資源を足す前に三つを確認します。揺れているのは抽出ノイズだけか。共同下落や悪い日の連続性を生成器はどれほど残したか。その予測期間に対応した標本外の校正は確かめたか。 シナリオ数を増やすことが直接答えるのは主として最初の問いです。

Block 20、Block 60、Student-tのどれを本番のリスク対策に採用するかも、この固定過去サンプルだけでは決めません。Block 20のACFが過去に似ていても未来のカバレッジは不明ですし、Student-tの厚い裾が95%VaRで必ず保守的とは限りません。まずリスク期間を決め、その期間のOOS検証を行うべきでしょう。

一番の収穫は、Gaussianの同時下落の差がシナリオ倍増では消えなかったことと、Circular Blockを追加した結果、最初の説明を言い直す必要があったことです。「百万件のリスクシナリオ」という言葉を見たら、どの現実の観測を繰り返し、肝心のリスク構造をどれだけ残したのかを聞くようにしています。

もっと小さな例から裾の統計量を確かめたい人には、vectorbtでNaNがCVaRの裾の標本分母を変える例もあります。Pythonの合成例を自分で計算してから、この重複窓の話に戻ると理解しやすいはずです。


データと固定条件:Kaggle Huge Stock Market Dataset v3。対象はSPY、IWM、EFA、EEM、TLT、IEF、LQD、HYG、GLD、DBC、UUP、VNQ。調整済み終値の共通日付は2007-04-11〜2017-11-10の2,668日で、欠損日は補完していません。固定CSV SHA-256:0a20c095f0d9400fbf7d30e17a0c664716f147c460b03bf5439cd980ef8bf38a。シナリオ実験は756日の学習リターンと50個の対応シード、別の単期VaR検証は1,911日です。過去データに基づくリスクモデリング手法の比較であり、投資助言ではありません。