0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

固定ホライズンのラベルは、なぜ筋が悪いのか——トリプルバリア法という選択肢

0
Posted at

この記事は Zenn に投稿したものの再掲です。初出: 固定ホライズンのラベルは、なぜ筋が悪いのか——トリプルバリア法という選択肢

以前、検証が嘘をつくパターンについて連載を書きました。あの連載は「検証をどう壊さないか」がテーマでしたが、今回は一歩手前の話です。そもそも、何を予測させるか——ラベル設計について書きます。

ラベルは、検証がどれだけ正しくても、設計そのものが悪ければ意味のあるモデルにはなりません。そして、この設計は驚くほど雑になりがちです。

一番よくある設計:固定ホライズンのリターン

もっとも素朴なラベルは、こういう形です。

# 「この先20本後の価格が今より上がっているか」を予測する
df["label"] = (df["close"].shift(-20) > df["close"]).astype(int)

シンプルで理解しやすい。でも、これには構造的な弱点が2つあります。

弱点1:利益と損失の非対称を無視する

固定ホライズンのラベルは「20本後にプラスか」しか見ていません。その途中で、含み損が実際の損切り幅を超えて死んでいても、20本後にたまたま戻っていれば「勝ち」としてラベル付けされます。逆に、序盤で大きく利確ラインに到達していても、20本後に反落していれば「負け」になる。

これは、実際のトレードのルール(利確・損切り)と、ラベルの定義が一致していません。 モデルは、あなたが実際には取らない挙動(「途中で何があっても20本後まで持ち続ける」)を学習することになります。

弱点2:ホライズンをどう決めても恣意的

「20本」という数字に、根拠はあるでしょうか。多くの場合、なんとなく決めた値です。銘柄やボラティリティが変われば、最適なホライズンも変わるはずなのに、固定値は市場の状態を無視します。ボラティリティが高い日に20本進む値幅と、静かな日に20本進む値幅は、まったく違う意味を持ちます。

代替案:トリプルバリア法

この2つの弱点に対する、比較的よく使われる解法がトリプルバリア法です。López de Pradoが体系化した手法として知られています。

発想はシンプルです。各エントリー候補点に対して、3本のバリアを引きます。

  1. 上側バリア(利確ライン)
  2. 下側バリア(損切りライン)
  3. 時間バリア(保有期限)

そして、どのバリアに最初に触れたかでラベルを決めます。

import pandas as pd
import numpy as np

def triple_barrier_label(
    prices: pd.Series,
    entry_idx: int,
    upper_pct: float,
    lower_pct: float,
    max_holding: int,
):
    """
    prices: 価格系列
    entry_idx: エントリー候補のインデックス位置
    upper_pct, lower_pct: エントリー価格からの上下バリア(%)
    max_holding: 時間バリア(最大保有本数)
    """
    entry_price = prices.iloc[entry_idx]
    upper = entry_price * (1 + upper_pct)
    lower = entry_price * (1 - lower_pct)

    window = prices.iloc[entry_idx + 1 : entry_idx + 1 + max_holding]

    for i, price in enumerate(window):
        if price >= upper:
            return 1, i + 1          # 利確バリアに先に到達
        if price <= lower:
            return -1, i + 1         # 損切りバリアに先に到達

    # 時間切れ:最終価格の方向で判定するか、0(判定不能)とする
    if len(window) == 0:
        return None, None
    final_return = (window.iloc[-1] - entry_price) / entry_price
    return (1 if final_return > 0 else -1), len(window)

これで得られるラベルは、実際のトレードルール(利確幅・損切り幅・保有期限)と定義が一致します。「20本後にプラスかどうか」ではなく、「利確ラインと損切りラインのどちらに先に触れたか」を直接ラベル化しているので、モデルが学習する対象と、実運用でモデルが使われる文脈がズレません。

バリア幅をどう決めるか:ボラティリティに連動させる

固定パーセンテージのバリアにも、固定ホライズンと同じ弱点が残ります。「上下2%」を全銘柄・全期間で固定すると、ボラティリティの高低を無視することになります。

改善策は、バリア幅をその時点のボラティリティに連動させることです。

def dynamic_barriers(prices: pd.Series, entry_idx: int, vol_window: int = 20, barrier_mult: float = 2.0):
    """直近のボラティリティに応じてバリア幅を決める"""
    returns = prices.pct_change()
    local_vol = returns.iloc[max(0, entry_idx - vol_window):entry_idx].std()
    barrier_pct = local_vol * barrier_mult
    return barrier_pct, barrier_pct  # 上下対称にする場合。非対称にしても良い

ボラティリティが高い局面ではバリアを広く、静かな局面では狭く取ることで、「どんな相場状況でも、同じような"意味のある値幅"を捉える」ラベルになります。

不均衡への対処

トリプルバリア法を含め、ラベルは往々にして不均衡になります。「時間切れ(0付近)」が多く、明確な利確・損切りに到達するケースが少ない、という分布になりがちです。

対処の選択肢はいくつかあります。

1. 時間切れを除外する

# 時間バリアで決着したサンプルを学習対象から外す
labeled = df[df["barrier_type"] != "time"]

もっともシンプルな方法です。ただしサンプル数が大きく減るので、データ量が少ない場合は注意が必要です。

2. サンプル重みで調整する

from sklearn.utils.class_weight import compute_class_weight

classes = np.unique(y_train)
weights = compute_class_weight("balanced", classes=classes, y=y_train)
class_weight = dict(zip(classes, weights))
# model.fit(..., class_weight=class_weight) のように渡す

除外せず、学習時に少数クラスの重みを上げる方法です。データを捨てないメリットがありますが、少数クラスへの過学習には注意してください。

3. メタラベリング(発生確率と方向を分離する)

これはLópez de Pradoが提案する、より発展的な手法です。「方向を当てるモデル」と「そのシグナルを取るべきかを判定するモデル」を分離します。

# 1段目:既存のルールベースやシンプルなモデルで方向シグナルを出す
primary_signal = simple_direction_model.predict(X)

# 2段目:そのシグナルが「取るべきか」を別モデルで判定する
# ラベルは「1段目のシグナルが正しかったか(0/1)」
meta_label = (primary_signal == true_direction).astype(int)
meta_model.fit(X, meta_label)

1段目のモデルの精度がそこそこでも、2段目が「自信のあるシグナルだけ絞り込む」役割を果たすことで、全体の精度と取引頻度のバランスを取れます。不均衡データへの対処としてだけでなく、シグナルの取捨選択そのものを学習させる発想として有効です。

ラベル設計とリークの関係(前回までの連載との接続)

ここで一つ、以前の連載と接続しておきます。トリプルバリア法のラベルは、**前向きの窓(未来のmax_holding本を見る)**で作られます。つまり、隣接するエントリー候補のラベルは時間的に重なりやすい。これは連載で扱った「特徴量リーク」の口の一つ、ラベルの窓の重なりそのものです。

トリプルバリア法を導入したら、purgeとembargoは必須だと考えてください。ラベル設計を工夫しても、分割の設計を疎かにすると、また同じ罠に戻ります。

まとめ

  • 固定ホライズンのラベルは、実トレードのルールと乖離しやすく、ホライズンの選び方も恣意的になりがち
  • トリプルバリア法は、利確・損切り・時間切れの3バリアで、実際のトレードルールと整合したラベルを作れる
  • バリア幅は固定値ではなく、ボラティリティに連動させると市場状況の違いを吸収できる
  • ラベルの不均衡には、除外・サンプル重み・メタラベリングという選択肢がある
  • トリプルバリア法のラベルは前向きの窓を持つため、purge/embargoは引き続き必須

ラベル設計は、モデルの精度以前に「何を学習させたいのか」を定義する工程です。ここが曖昧なままハイパーパラメータをいくら調整しても、土台がズレたままです。


質問や「うちはこういうバリア設計にしている」という話があればコメントで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?