リターンを数千回シャッフルしたあと、あなたの成績はどこに位置するのか——そしてこの数字が答えないこと。
バックテストが終わり、Sharpe はまずまずに見えます。しかし心の奥にある疑問はまだ答えられていません。戦略が本当に何かを捉えたのか、それともたまたま上昇相場の中で適当に切っただけなのか。これに答える方法のひとつが、データを数千回シャッフルして、その乱数の山の中で自分の成績が何番目に来るかを見ることです——ワークスペースのバックテストタブにある「MCPT p 値」の行がやっているのは、まさにこれです。この記事では、それが何を検定し、どう読み、そして合格したあとも答えていない疑問は何かを扱います。
MCPT(Monte Carlo Permutation Test、モンテカルロ順列検定)は統計的検定です。バックテスト期間のリターン系列を何度もランダムに並べ替え、そのたびに同じエントリー・エグジットの位置で Sharpe を計算し直して、「純粋な運ならどんな形になるか」という分布を丸ごと作り、そのうえで実際の成績がその分布のどこに落ちるかを見ます。
| 項目 | 内容 |
|---|---|
| 帰無仮説 | この戦略が選んだリターン期間は、ランダムに選んだものより優れていない |
| p 値の定義 | シャッフル後の Sharpe が実際の Sharpe 以上になった割合 |
| 読み方 | p < 0.05 → 95% の信頼水準で統計的に有意な優位性がある |
| 検定の範囲 | バックテストデータ全体。学習/テストの分割なし |
p 値が小さいほど、「適当に並べ替えただけであなたに勝てる」ケースが稀だということです。既定の 2000 回を例にすると、p = 0.05 はこの 2000 回のうち 100 回の乱数成績があなたに負けていないことを意味し、p = 0.005 なら 10 回だけということです。
これは設計全体の中で最も重要で、最も誤解されやすい点です。並べ替えられるのはフォワードリターン系列であり、あなたのポジション系列は最初から最後まで固定されています。
各順列がやっていること: position(あなたのポジション) ← 固定。ループの外で一度だけ計算 × vol_scalar(ポジションのスケーリング) ← 固定。ループの外で一度だけ計算 − fee_cost(手数料) ← 固定。ループの外で一度だけ計算 × シャッフル後のリターン系列 ← 毎回引き直すのはこれだけ = この順列の Sharpe
手数料もポジションのスケーリングもループの外で計算し終えているため、どの順列も負う取引コストとレバレッジは同じです。残る唯一の変数は「リターンが現れる順序」——つまりあなたが選んだタイミングは、ランダムに選んだタイミングより良いのかということです。
それではサービス問題になってしまうからです。二値のポジション配列を並べ替えると、戦略自身よりはるかに多いエントリー・エグジットの切り替えが生まれます。fee = 0.0005 のもとでは、どの順列もおよそ30 〜 40 倍の手数料の重しを負い、並べ替え後の Sharpe はすべて深いマイナスへ押し込まれます。あなたの戦略に本物の優位性があろうとなかろうと、p 値は無意味なほど美しく出てしまいます。
これは自動であって、オプションではありません。Type A 戦略のバックテストのたびに一度実行され、結果はそのままバックテスト統計に書き込まれ、ワークスペースのバックテストタブに表示されます。
| 設定 | 既定値 | 説明 |
|---|---|---|
| 順列回数 | 2000 | 戦略側で MCPT_N により上書きできますが、下記の実行予算の式の制約は受けます |
| 乱数シード | 42 | プライベートな乱数生成器を使い、グローバルのシードには触れません——同じバックテストからは常に同じ p 値が得られます |
| 手数料 | 戦略自身の FEE | バックテストが使うものをそのまま渡します |
| オフにする | MCPT = False | 丸ごとスキップし、MCPT のフィールドは一切書きません |
覚えておく価値のある挙動が 2 つあります。パラメータ探索の最中には MCPT は実行されません(1 回の探索は数十から数百回のバックテストであり、加えると使い物にならないほど遅くなるため)。したがってあなたが目にする p 値は常に「パラメータを採用したあとのその 1 回のバックテスト」から来ています。もうひとつ、戦略が稼働に入ったあとは、ライブでもスケジュールでも、トリガーのたびに同じ MCPT のフィールドがそのまま引き継がれます——同じコード、同じパラメータなので p 値はなお成立し、だからこそ稼働中の戦略にもずっと表示され続けます。
ワークスペースのバックテストタブは合否を判定しません。数字を表示し、説明を 1 行添えるだけです。p < 0.05 のときはこう書かれます:
通らなかったときはこう書かれます:
本当の「合否」は戦略ライブラリの 3 つの品質基準に現れます。基準 2 は「統計的に有意」と呼ばれ、説明には「MCPT 並べ替え検定 p < 0.05」と書かれていますが、実際の判定は2 つの条件が同時に成立することを求めます:
| 条件 | 基準 |
|---|---|
| p 値 | < 0.05 |
| 順列回数 | ≥ 1000 |
| ポートフォリオ戦略(Type C) | 「MCPT 対象外(ポートフォリオ戦略)」と表示され、不合格とは扱われません |
3 つの基準がすべて合格または対象外のときに初めて、戦略に「検証済み」バッジが付きます。役割分担をはっきり言うと、あなたのサーバーは p 値と順列回数を計算するだけで、「これで合格と見なすか」は掲載側の判断です。
ワークスペースに付属するチュートリアル用サンプル btc_sma_cross を一度走らせます(BTCUSDT 1h、2022-01-01 から、SMA_FAST = 45/SMA_SLOW = 100、パラメータは一字も変えず、41,287 本のバー)。以下の数字はすべて 2026-09-17 のその実行から来ています。
| バックテスト | 値 |
|---|---|
| 総リターン | +123.95% |
| ベンチマーク(バイ・アンド・ホールド) | +64.36% |
| 最大ドローダウン | −44.93% |
| Sharpe Ratio | 0.6751 |
| 取引回数 | 478 |
| 支払済み手数料(元本比) | 23.90% |
| MCPT | 値 |
|---|---|
| p 値 | 0.0160 |
| 順列回数 | 2000 |
| ヒストグラムの赤線「実際のシャープレシオ」 | 0.8985 |
| 分布の範囲 | −0.8565 ~ 1.4218 |
バックテストが自身で標準出力に印字する行はこれです:
MCPT p-value: 0.0160 (n=2000, significant edge at 95%)
読み方:p = 0.0160 は、2000 回のランダムシャッフルのうち 32 回が同等以上の Sharpe を出したという意味です——言い換えると、このエントリーとエグジットのタイミングは、この歴史の区間においては適当に選んだようには見えません。順列回数 2000 も基準の 1000 を上回っているため、この項目は合格です。
ここが本記事で最も重要な節です。上記の戦略は同じ日、同じコードのもとで、walk-forward のアウトオブサンプル検証も別途実行しました:
| 検定 | それが問うている問い | 2026-09-17 の実行 | 結果 |
|---|---|---|---|
| MCPT | この固定されたポジション系列の上で、この成績は実力と運を区別できるか? | p = 0.0160(n = 2000) | 合格 |
| Walk-forward | 過去データからパラメータを選ぶこと自体が、次の区間でも通用するか? | アウトオブサンプル効率 −0.334(アウトオブサンプル Sharpe −0.45) | 基準を大きく下回る |
片方は通り、片方は通らない。しかもどちらも計算違いではありません——そもそも同じ問いに答えていないからです。MCPT が検定しているのは「すでに決まったこのエントリーとエグジットが、この歴史の区間に置いたときに当てずっぽうには見えない」ことであり、walk-forward が検定しているのは「過去データでパラメータを選ぶというこの行為が、見たことのない次の区間でも成立するか」です。前者の合格は後者を含意しません。
これはこの戦略が壊れていると言っているのではありません——ワークスペースに付属するチュートリアル用サンプルであり、要点は 2 つの検定の意味の違いです。walk-forward の完全な数字と読み方は〈アウトオブサンプル検証のやり方〉をご覧ください。
agent に MCPT のヒストグラムを描かせてチャットに送らせると、その図の凡例には次の 1 行が印字されます:
Actual OOS Sharpe = ⟨あなたの数字⟩
OOS は out-of-sample(アウトオブサンプル)の略です。この行は古いコメントの名残のラベルであって、文字どおりに理解してはいけません。 MCPT は最初から最後までバックテストデータ全体の上で走り、学習期間もテスト期間も分割比率も一切ありません——アウトオブサンプルの何かであるはずがなく、上の対照表がその実例です。
ワークスペースのバックテストタブにある図にはこの問題はなく、赤線には「実際のシャープレシオ」と書かれています。なお、チャットに出てくる図は手動で再実行して生成したものであり、手動の再実行で使うパラメータは自動のときと必ずしも同じではないため、図中の数字がタブのその行と一致するとも限りません。
自動 MCPT が実際に何回走るかは、完全にはあなたが決められません。実行予算の式があります:
実際の回数 = min( MCPT_N, 20000, max( 200, 4e8 ÷ バー本数 ) )
バーが多いほど 4e8 ÷ バー本数 は小さくなります。上記の実行は 41,287 本のバーで、上限からはまだ遠いため、2000 回は一度も削られていません。ただし公式ドキュメントに記載された対照はこうです:40,000 本 → 2000 回。200,000 本(5 分足 2 年)→ 2000 回。1,000,000 本(1 分足 2 年)→ 400 回。
問題は、掲載の品質基準が順列回数 ≥ 1000 を求めることです。式から逆算すると、バー本数がおよそ 400,000 本を超えた時点で実際の回数は 1000 を下回ります——たとえ p = 0.001 でも、基準は容赦なく不合格と判定します。しかも画面には何の手がかりもありません。削られたというメッセージはサーバーのログにしか書かれず、ワークスペースの説明文は削られたあとの回数を平然と差し込み、警告のスタイルも一切ありません。
上の 2 つの表を見返してください。同じバックテストなのに、Sharpe Ratio のカードは 0.6751、ヒストグラムの赤線「実際のシャープレシオ」は 0.8985 と書かれており、0.22 の差は肉眼でも分かります。これはバグではなく、2 つの異なる計算方法です:
| 項目 | MCPT 内部の Sharpe(0.8985) | バックテストタブの Sharpe Ratio(0.6751) |
|---|---|---|
| リターンの計算方法 | 単純リターン。寄り付きと引けの区間を分けない | オーバーナイトの区間と日中の区間を分けて価格評価 |
| ポジションのスケーリング | 必ず適用:30% の目標ボラティリティ、2 倍のレバレッジ上限 | 戦略が自分で書いた場合のみ |
肝心なのは 2 行目です。自動 MCPT は戦略自身の目標ボラティリティ設定を渡さず、常に関数の既定値を使います。btc_sma_cross はボラティリティターゲティングをしていませんが、その MCPT の Sharpe はやはり「30% の目標ボラティリティと 2 倍の上限を適用したあと」の数字です(目標ボラティリティが何をしているかは〈ボラティリティターゲティングの仕組み〉をご覧ください)。2 つの数字が違うのは正常で、インターフェースは今のところこの件を説明していません。
失敗しても静かです。エラーメッセージは出ず、バックテストはいつもどおり成功し、フロントエンドにその行だけがありません。原因は少なくとも 4 種類あり、画面はそれらを区別しません:
| 原因 | 何が起きたか |
|---|---|
| 戦略に MCPT = False と書いた | そのままスキップ |
| このバックテストの取引が 0 件 | 検定するポジションがないためスキップ |
| サーバー上の lib が旧バージョン | 読み込みに失敗しスキップ |
| データが短すぎる、Sharpe が NaN になる、またはあらゆる例外 | スキップ。バックテスト統計はいつもどおり書き込み |
間違って書くくらいなら書かないという理由は、とても現実的です。NaN や inf がひとつでもあるとサーバー全体の戦略アップロードが弾かれてしまうため、数字がきれいでない限り、これらのフィールドはまとめて書かれません。
Type C(ポートフォリオ戦略、N 銘柄にウェイトを配分)のバックテストでは MCPT は実行されません。この検定の構造が「1 本の価格系列に対する 1 本のポジション系列」であり、ポートフォリオ戦略にはその構造がないからです。正直に言っておく価値があるのは、代わりの検定はないということです。
| やりたいこと | 実際のところ |
|---|---|
| ポートフォリオ全体を 1 本のリターン系列に潰して再抽出する | それは実現リターンに対するブートストラップであり、別の問いに答えるものです。しかもp 値を生み出せません——p 値こそが MCPT の存在意義のすべてです。この道は明文で禁止されており、agent が自分で何かを手作りして代用することも許されていません |
| 基準のカードに何が表示されると思うか | 「MCPT 対象外(ポートフォリオ戦略)」であって「不合格」ではありません——不合格とは扱われず、「検証済み」バッジにも影響しません |
順列回数はいくつか? 説明文にあるその回数が 1000 を下回っていれば、この p 値はどれだけ小さくても掲載の品質基準を通りません。回数を先に、p 値は後に見てください。
これはどのバックテストの p 値か? パラメータ探索の過程では MCPT は実行されません。あなたの手元の p 値は「パラメータを採用したあとに走らせたその 1 回のバックテスト」だけのものです。途中でコードを変えたなら、実行し直さなければ有効になりません。
赤線とカードが合わない? 正常です。上記の実行は 0.8985 と 0.6751 で、計算方法が違うだけです。デバッグしに行く必要はありません。
「有意」を「儲かる」と読んでいませんか? 同じ戦略が p = 0.016 で合格し、アウトオブサンプル効率 −0.334 で惨敗することもあります。p 値は未来について何も述べていません。
その行がそもそも見当たらない? まず 4 種類の原因のどれかを確かめてください(戦略でオフにした/取引 0 件/lib が旧バージョン/データ不足)。「検定が通らなかった」と受け取らないでください。
p 値が分かったら、次の問いは上の対照表の右半分です。見たことのないデータに変えても成立するのか。その道がアウトオブサンプル検証で、ワークスペースには専用のタブがあります。戦略ライブラリで「検証済み」バッジが付いている公式戦略が基準 2 で通っているのは、まさに本記事のこの検定です——どれも実際のバックテストが付いており、数字はそのまま見られます:戦略ライブラリ。