Skip to content
Go back

VWAP乖離エントリーフィルター:「寄天」損失を構造的に排除する

Edit page

日本株のシステムトレードで頻発する「寄天」(寄り付き天井)損失を、VWAP(出来高加重平均価格)乖離フィルターで構造的に排除する。VWAPを大幅に上回る水準での買いエントリーは統計的に負の期待リターンとなるため、ペナルティまたはブロックを適用する。

JS-E5施策では2段階の閾値(通常2%/重度5%)を設定し、penalty(スコア減算)とblock(エントリー拒否)の2モードを実装した。


「寄天」問題とは

典型的な損失パターン

株価

  │    ★ 寄付(始値)= 本日最高値
  │   ╱╲
  │  ╱  ╲
  │ ╱    ╲
  │╱      ╲───── VWAP
  │        ╲
  │         ╲
  │          ╲
  │           ★ 終値
  ├──────────────→ 時間
  9:00        15:00

テクニカルスコア: 8.2/10(前日の分析で算出)
寄付で買い注文 → VWAPから+3.5%乖離
→ 当日中にVWAPに収束 → 損切り発動

前日のテクニカル分析で強い買いシグナルが出ていても、翌朝の寄付が異常に高い場合、そこからの下落で損失を被る。VWAPからの乖離率が大きいほど、この「平均回帰」のリスクが高い。


VWAP乖離率によるフィルタリング

VWAP(出来高加重平均価格)とは

# VWAPの計算
VWAP = Σ(Price_i × Volume_i) / Σ(Volume_i)

# 乖離率の計算
deviation_pct = (current_price - prev_vwap) / prev_vwap * 100

VWAPは「その日の取引の平均的な約定価格」を表す。VWAPを大幅に上回る価格で買うことは、「市場参加者の平均より割高に買う」ことを意味する。

2段階フィルタリング

乖離率        判定           アクション
──────────────────────────────────────
0〜2%        正常           そのまま通過
2〜5%        注意(penalty) スコア -1.5
5%〜         危険(severe)  スコア -3.0(またはblock)

実装

VwapDeviationFilter

# _jsTradingEngine/core/vwap_deviation_filter.py

class VwapDeviationFilter:
    def __init__(self, config: VwapDeviationFilterConfig):
        self._config = config

    def calculate_deviation_pct(
        self, current_price: float, prev_vwap: float
    ) -> float:
        """VWAP乖離率を計算"""
        if prev_vwap <= 0:
            return 0.0
        return (current_price - prev_vwap) / prev_vwap * 100

    def get_score_penalty(
        self, current_price: float, prev_vwap: float
    ) -> float:
        """ペナルティスコアを返す(0以下の値)"""
        if not self._config.enabled:
            return 0.0

        deviation = self.calculate_deviation_pct(current_price, prev_vwap)

        # 買いの場合のみフィルタリング(VWAPより高い=割高)
        if deviation <= 0:
            return 0.0

        if deviation >= self._config.severe_threshold_pct:
            return self._config.severe_penalty_score  # -3.0
        elif deviation >= self._config.deviation_threshold_pct:
            return self._config.penalty_score  # -1.5

        return 0.0

    def check_entry_allowed(
        self, current_price: float, prev_vwap: float
    ) -> bool:
        """エントリーを許可するか(blockモード用)"""
        if not self._config.enabled:
            return True

        if self._config.filter_mode != "block":
            return True  # penaltyモードでは常に許可

        deviation = self.calculate_deviation_pct(current_price, prev_vwap)

        if deviation >= self._config.severe_threshold_pct:
            logger.warning(
                f"[JS-E5] BLOCKED: VWAP deviation {deviation:.1f}% "
                f">= {self._config.severe_threshold_pct}%"
            )
            return False

        return True

トレードエンジンへの統合

# _jsTradingEngine/core/trading_engine.py(統合部分)

class TradingEngine:
    def _check_entry_filters(
        self, signal: TradeSignal, market_data: MarketData
    ) -> tuple[bool, float]:
        """エントリーフィルターを適用"""
        total_penalty = 0.0

        # JS-E5: VWAP乖離フィルター
        if self._vwap_filter is not None:
            # blockモードチェック
            if not self._vwap_filter.check_entry_allowed(
                market_data.open_price, market_data.prev_vwap
            ):
                return False, 0.0

            # penaltyモードチェック
            penalty = self._vwap_filter.get_score_penalty(
                market_data.open_price, market_data.prev_vwap
            )
            if penalty < 0:
                logger.info(
                    f"[JS-E5] {signal.code}: VWAP penalty={penalty:.1f}, "
                    f"deviation={self._vwap_filter.calculate_deviation_pct(market_data.open_price, market_data.prev_vwap):.1f}%"
                )
            total_penalty += penalty

        # 他のフィルターも同様に適用...

        return True, total_penalty

パラメータ設計

{
  "vwap_deviation_filter": {
    "enabled": false,
    "filter_mode": "penalty",
    "deviation_threshold_pct": 2.0,
    "penalty_score": -1.5,
    "severe_threshold_pct": 5.0,
    "severe_penalty_score": -3.0
  }
}

penalty vs block の使い分け

モード動作適するケース
penaltyスコアから減算。他の要因が強ければエントリー可通常運用(推奨)
block重度乖離時にエントリー完全拒否保守的な運用

penalty モードでは、VWAP+3%乖離でもテクニカルスコアが十分高ければ(スコア >= 1.9 + 1.5 = 3.4)エントリー可能。本当に強いシグナルまでは排除しない。


テスト戦略

テスト構成(53 unit + 22 integration + 3092 regression):
├── calculate_deviation_pct テスト(10件)
│   ├── 正の乖離(割高)
│   ├── 負の乖離(割安)
│   ├── ゼロ乖離
│   ├── prev_vwap = 0 の防御
│   └── 大きな乖離値
├── get_score_penalty テスト(15件)
│   ├── 閾値未満(ペナルティなし)
│   ├── 通常閾値境界(2.0%)
│   ├── 重度閾値境界(5.0%)
│   ├── 売り方向(フィルタリングなし)
│   └── enabled=false で常に0
├── check_entry_allowed テスト(12件)
│   ├── penaltyモード(常にTrue)
│   ├── blockモード + 閾値未満
│   ├── blockモード + 重度乖離
│   └── enabled=false で常にTrue
├── 統合テスト(16件)
│   ├── TradingEngineでのフィルター適用
│   ├── 他フィルターとの併用
│   ├── バックテストエンジン対応
│   └── ログ出力確認
└── 回帰テスト
    └── 既存3,092テスト全件PASS

期待効果

指標BeforeAfter(予測)
勝率48%51〜53%(+3〜5%)
寄天による損失月2-3回月0-1回
見送りトレード数なし月1-2件(VWAP乖離で除外)

まとめ

  1. VWAP乖離率で「寄天」損失を構造的に排除する。2段階の閾値(通常2%→ペナルティ-1.5、重度5%→ペナルティ-3.0)により、VWAPを大幅に上回る水準での買いエントリーを抑制

  2. penalty(スコア減算)とblock(エントリー拒否)の2モードを実装。推奨はpenaltyモードで、テクニカルスコアが十分に強いシグナルまでは排除しない設計

  3. enabled=false デフォルトで後方互換性を完全維持。53 unit + 22 integration + 3,092 regression テスト全件PASS。バックテスト結果に基づき有効化を判断


関連記事


Edit page
Share this post on:

Previous Post
米国株のリスク管理施策群:VIXテール保険からギャップリスクまで
Next Post
ザラ場動的キャンセル機構:発注後の相場急変に自動対応する