Nグループからk個をテスト
T個の時系列標本を、シャッフルせずN個の連続グループG1,…,GNへ分けます。各splitではkグループをテスト、残りN−kを学習にします。
kを増やすとテストの多様性は増えますが、各モデルの学習量は減ります。通常はk≤N/2とし、学習・テストの取引数が十分かを先に確認します。
分割数と経路数
Nグループからk個を選ぶため、train/test split数は二項係数です。
すべてのsplitにはk個のテストグループがあるため、テストグループ出現総数はkS。各完全経路はNグループを1回ずつ使うので、構成可能な経路数φは次です。
例としてN=6、k=2なら、splitはC(6,2)=15、経路はφ=5、学習比率は4/6です。
CPCVのアルゴリズム
- 履歴をシャッフルせずN個の連続グループへ分ける。
- テストグループk個の全組合せを列挙する。
- 各splitでラベル区間がテストと重なる学習標本をpurgeする。
- 各テスト境界へ事前定義のembargoを置く。
- 標準化、特徴選択、モデルfitをtrainだけで行う。
- テスト予測とコスト後損益を、元の時刻とsplit ID付きで保存する。
- テストグループを割り当て、φ本の時系列OOS経路へつなぐ。
from itertools import combinations
groups = range(N)
for test_groups in combinations(groups, k):
train_groups = set(groups) - set(test_groups)
train_idx = purge_and_embargo(train_groups, test_groups, intervals)
model = fit_pipeline(X[train_idx], y[train_idx])
pred = model.predict(X[index_of(test_groups)])
save_oos(pred, test_groups)同じテストグループでも学習組合せが違えば予測が変わるため、グループとsplitの組を保存します。
経路を組み立てる
各グループはφ回テストに登場します。各登場を1〜φの経路へ一度ずつ割り当て、各経路がG1からGNまでを時系列順に1回ずつ含むようにします。
⊕は時系列連結です。経路の各区間は異なる学習モデルから生まれます。これは実際の一回の運用履歴を完全再現するものではなく、分割感度を見る再構成です。
分布の読み方
φ本それぞれについて、同じ定義で指標を出します。
- コスト後期待値、Sharpe、Sortino、Calmar
- 最大DD、回復期間、下位5%損失
- 取引数、売買回転率、ポジション集中
- 利益が正の経路比率、Sharpeが基準未満の経路比率
平均だけでなく、中央値、10%点、最悪経路を報告します。採用基準を結果分布を見た後で作りません。
限界
- CPCVは同じ有限履歴を組み替えるだけで、新しい市場データを作らない。
- 経路は学習データとテスト区間を共有するため、互いに独立ではない。
- Nとk、Purging、Embargoの設計を結果で選べばメタ過学習になる。
- 小標本で分割を増やすと、各trainとtestが薄くなる。
- 単一のルールベース戦略で再学習がない場合、何を各splitでfitするかを明確にする必要がある。
ウォークフォワードは実際の更新順序を再現しやすく、CPCVは分割から生じる成績分布を調べます。目的が違うため、どちらか一方を万能としません。
- N・k・分割規則を事前固定した
- 各splitでPurging・Embargoを実施した
- 前処理をtrainだけでfitした
- 経路構成表を保存した
- 中央値・下位分位・失敗率を報告した
- 経路を独立標本と呼んでいない
ミニテスト
4問で確認します。
Q1. N=6、k=2のsplit数は?
C(6,2)=15です。
Q2. N=6、k=2の経路数φは?
(2/6)×15=5です。
Q3. 各splitで必要なことは?
テスト情報を学習へ混ぜません。
Q4. CPCV経路の正しい理解は?
共有履歴から構成され、独立ではありません。
参考資料
Marcos López de Prado, Combinatorial Purged Cross-Validation および Advances in Financial Machine Learning Chapter 12。