v13 — Exit Strategy Expansion

배경

  • v11에서 19+ exit 변형을 이미 테스트했고 (research_exit, research_exit_v2, grid_search_v5), 그 상위 5개가 v11 final에 올라감
  • v11 best (bb_pullback_12 × chandelier_atr3, ens_v51 × fixed_70_30_180) Sharpe 2.0 전후 도달 → 더 끌어올리려면 기존 카테고리(fixed/trailing/scaled/breakeven)의 변주가 아니라 새로운 exit 차원을 도입해야 함
  • v13은 “exit 리서치의 다음 세대”. v11 최강 엔트리들 위에 새로운 exit 컨셉 8개를 얹어 개별 실험

공통 스펙 (v13.1 ~ v13.8 모두 동일)

엔트리 5종 (v11 동일)

#이름생성 함수설명
1bb_20detect_bottom_bounce(bounce_pct=0.20)20% 반등
2bb_30detect_bottom_bounce(bounce_pct=0.30)30% 반등
3hammerdetect_hammer_candle해머 캔들
4bb_pullback_12detect_bounce_pullback(bounce=0.30, pullback=0.12)반등 후 12% 되돌림
5ens_v51merge_or_signals([bb_30, hammer])bb_30 ∪ hammer

포트폴리오

V13_PORTFOLIO = {
    "initial": 100_000, "max_pct": 0.125, "max_concurrent": 8,
    "cooldown_days": 90, "slippage_per_side": 0.005,
    "commission_per_trade": 0.0, "max_volume_pct": 0.10,
}

가격 필터

  • p3 고정 ($3 이하만 진입). v11에서 mean OOS Sharpe 최강(1.81). 다른 값과 섞으면 v13.N끼리 비교 불가능해짐

OOS 분할

  • 2024-01-01 (v11 동일)

Baseline 비교

  • 각 엔트리마다 v11 best exit을 같은 실행에 넣어 A/B 비교
    • bb_20 → fixed_30_50_180
    • bb_30 → fixed_70_30_180 (추정, 없으면 fixed_100_40_365)
    • hammer → fixed_70_30_180
    • bb_pullback_12 → chandelier_atr3
    • ens_v51 → fixed_70_30_180

산출물 (각 v13.N마다)

results/v13_N/
├── report.md          # summary, top picks, charts
├── results.csv        # 전체 10행 (new exit 5 + baseline 5)
├── trade_log.csv      # 개별 체결 기록
├── equity_curves.png
└── meta.json

실행 스크립트

  • biotech/backtest/scripts/run_v13_1.py ~ run_v13_8.py
  • 공통 로직: v13_common.py (엔트리 5개 생성, baseline 정의, 결과 저장)

v13.1 — Catalyst Event-Window Exit (PDUFA/readout D-N 청산)

가설: 바이오 주가는 FDA PDUFA/Phase readout 같은 이벤트 앞에 프리미엄이 형성되고, 이벤트 당일/직후 IV crush로 수익을 반납하는 경우가 많음. 이벤트 전에 먼저 빠져나오면 수익 고정 가능.

로직:

  • 엔트리 시점에 해당 symbol의 가장 가까운 catalyst date 조회
  • catalyst date - N일이 되면 종가 강제 청산
  • 일반 SL/max_hold도 병행
  • grid: N = 3 / 5 / 10

필요 데이터 🔴

  • 각 종목별 확정된 PDUFA date, Phase readout date
  • 현재 clinical_trials 테이블에 primary_completion_date, estimated_completion_date는 있지만 PDUFA date는 따로 스크랩 필요
  • readout date는 Phase 2/3 completion + 3090일 공시 지연 추정 가능 but noisy

데이터 요구사항 (Dan 진행)

  1. PDUFA 데이터 — FDA 공식 캘린더, BioPharma Catalyst (https://www.biopharmcatalyst.com/calendars/fda-calendar), FDA Tracker 등에서 수집
    • 필드: symbol, drug, pdufa_date, indication, catalyst_type
    • 기간: 2017~2025 (백테스트 전 구간)
  2. Phase readout 데이터 — 마찬가지로 biopharmacatalyst.com 또는 PR newswire 수집
    • 필드: symbol, trial_nct_id, readout_date, phase, outcome

구현 🟢 (데이터 도착 시)

  • 신규 engine: simulate_catalyst_pre_event_exit(signals, prices, catalysts, days_before=3)

v13.2 — Post-Catalyst IV Crush Exit

가설: 이벤트 당일은 변동성 최대치. 이벤트 종료 직후(다음 거래일) 종가에 빠지면 IV crush 이전의 프리미엄까지 잡을 수 있음 vs 일반적으로는 가격 되돌림 발생.

로직:

  • catalyst date + 1 거래일 종가에 강제 청산
  • 단 catalyst date 이전에 다른 exit 룰이 trigger되면 그대로 청산

필요 데이터 🔴

  • v13.1과 동일 (PDUFA / readout date)

구현 🟢

  • 신규 engine: simulate_catalyst_post_event_exit(signals, prices, catalysts, days_after=1)

v13.1 vs v13.2 비교 포인트: “이벤트 전 털기”와 “이벤트 후 털기” 중 어느 쪽이 EV가 높은지 판가름. 보통 Phase 3 readout 전 털기가 유리, Phase 2 readout 후 털기가 유리할 가능성 있음 (데이터로 검증).


v13.3 — Profit-Gated Time Stop 🟢 즉시 구현 가능

가설: 현재 max_hold 룰은 수익/손실 상태와 무관하게 기계적으로 종료. “이기고 있을 때 좀 더, 지고 있을 때 빨리” 원칙 검증.

로직:

  • 엔트리 후 기본 max_hold (fixed_70_30_180 기준 180일)
  • max_hold 도달 시점의 수익률에 따라 분기:
    • 수익 ≥ +20%: +30일 연장 (한 번만)
    • 수익 < 0%: 즉시 청산 (max_hold까지 기다리지 않음)
    • 0% ~ +20%: 기존 max_hold
  • grid: (hold_base, extend_days, profit_threshold) = (180, 30, 0.20) / (180, 60, 0.20) / (180, 30, 0.10) / (365, 60, 0.30)

필요 데이터: 없음 — 기존 가격 데이터만 사용

구현

  • 신규 engine: simulate_profit_gated_time_exit(signals, prices, max_hold, extend_days, profit_threshold, sl, tp)

v13.4 — Chandelier Multi-Scale (보유일수별 ATR 계수 변경) 🟢

가설: 엔트리 초반은 엔트리 실패 확률이 높아 타이트한 stop 필요. 후반에 도달한 trade는 winner일 가능성이 높아 느슨하게 두는 게 EV 높음.

로직:

  • 기존 simulate_atr_chandelier_exitatr_multiplier를 상수가 아닌 보유일수 함수
  • 설정: days 030 → ATR×2.0, days 3160 → ATR×3.0, days 61+ → ATR×4.0
  • grid: (2.0, 3.0, 4.0) vs (2.5, 3.5, 5.0) vs (1.5, 3.0, 5.0)

필요 데이터: 없음

구현

  • 기존 simulate_atr_chandelier_exit 확장 또는 신규 simulate_chandelier_multiscale_exit

v13.5 — XBI Relative-Strength Exit 🟡

가설: 종목이 섹터 대비 지속적으로 underperform 하면 종목 이슈보다 섹터 이슈일 가능성은 낮고, 이미 edge가 날아간 경우가 많음. 섹터 대비 약세면 컷.

로직:

  • 엔트리 후 매일 rolling 20-day return 비교:
    • stock_20d_return - XBI_20d_return < -10%p → 다음날 시가 청산
  • grid: 5%p / 10%p / 15%p, lookback 10d / 20d / 30d

필요 데이터 ✅ 이미 있음

  • benchmarks 테이블에 XBI (2015-01-02 ~ 2026-04-17, 2838 rows) 이미 수집됨
  • IBB 도 같이 있어서 large-cap 바이오 대비 비교 실험 가능
  • VIX 도 있어서 v13.8 overlay에 활용 가능

구현 🟢 (데이터 도착 시)

  • 신규 engine: simulate_relative_strength_exit(signals, prices, xbi_prices, lookback, threshold)

v13.6 — Volatility Expansion Stop 🟢

가설: 급격한 TrueRange 확장은 패닉 / 테이프 붕괴의 신호. 통상 다음 날 이어지는 경우가 많음 → 먼저 빠지는 게 EV 유리.

로직:

  • 엔트리 후 매일 TrueRange 계산
  • TR_today > ATR_20 × 2.5 이면 다음날 시가 청산
  • grid: 2.0 / 2.5 / 3.0 배수

필요 데이터: 없음

구현

  • 신규 engine: simulate_vol_expansion_exit(signals, prices, atr_period=20, multiplier=2.5)

v13.7 — MA Dead-Cross Exit 🟢

가설: 고전적인 시그널이지만 바이오처럼 변동성 큰 자산에서 “추세 종료” 확인용으로 동작. baseline 대비 얼마나 빠지는지 측정 자체에 가치.

로직:

  • 10-day SMA < 50-day SMA 로 전환되는 첫 봉에 다음날 시가 청산
  • grid: (10, 50) / (20, 50) / (5, 20) / (10, 30)

필요 데이터: 없음

구현

  • 신규 engine: simulate_ma_crossover_exit(signals, prices, fast=10, slow=50)

v13.8 — Portfolio-Level Kill-Switch Overlay 🟡

가설: 개별 종목 exit만으로는 섹터 붕괴 국면 방어 불가. 포트폴리오 레벨 overlay가 MDD를 극적으로 줄일 수 있음.

로직:

  • 개별 exit 은 v11 best 유지
  • 추가 overlay 룰:
    • XBI 20일 drawdown > 15% → 신규 진입 OFF + 전 포지션 1/2 축소 (각 포지션 50% 청산)
    • XBI 20일 drawdown이 10% 이하로 회복되면 신규 진입 재개
  • grid: DD threshold 10% / 15% / 20%

필요 데이터 🟡

  • v13.5와 동일 (XBI 일봉)

구현 🟢 (데이터 도착 시)

  • 포트폴리오 시뮬레이터 확장 필요: simulate_portfolio_with_overlay(trades, price_lookup, xbi_prices, overlay_rules)
  • 이건 기존 단일 trade 기반 exit과 다르게 포트폴리오 레벨 개입이므로 engine/portfolio.py 수정 필요

Dan 데이터 작업 요약

데이터용도상태
XBI / IBB / VIX 일봉v13.5, v13.8benchmarks 테이블에 이미 있음
Phase readout baselinev13.1, v13.2🟡 clinical_trials.primary_completion_date 로 먼저 실험 (announced_date = completion_date - 90d 가정)
PDUFA 캘린더v13.1, v13.2🔴 새로 수집 필요 (biopharmcatalyst.com / FDA Tracker) — Phase readout baseline 결과 좋으면 그때 투자

데이터 없이도 v13.3 / v13.4 / v13.5 / v13.6 / v13.7 / v13.8 6개는 즉시 실행 가능 (benchmarks 활용). 이 6개가 Step 1. Step 2에서 catalyst 데이터 보강 후 v13.1 / v13.2 추가 실행.


실행 단계

Step 0 (이미 수집): v11 엔트리 재사용 확인 ✅ Step 1 (🟢 즉시, 23h): v13.3, v13.4, v13.6, v13.7 구현 + 실행 + 리포트 생성 Step 2 (🟡 XBI 데이터 도착 시): v13.5, v13.8 구현 + 실행 Step 3 (🔴 catalyst 데이터 도착 시): v13.1, v13.2 구현 + 실행 Step 4: 8개 결과 통합 메타-리포트 — 어느 exit 아이디어가 어느 entry에 가장 잘 맞는지 매트릭스 정리


성공 기준

  • 최소 한 개 이상의 (entry × new exit) 조합이 baseline(v11 best) 대비 OOS Sharpe 개선 OR MDD 20% 이상 축소
  • 전체 8개 중 2~3개는 “의미 없음” 결론 나와도 OK — 새로운 차원 시도 자체가 가치

리스크 / 주의

  1. p3 고정으로 인한 trade 수 제한: v11에서 p3 조건 trade 수가 최소 16에서 최대 31 수준. v13 신규 exit 룰 중 일부가 “룰 발동 빈도 낮음” 문제 마주칠 수 있음 → trade 수가 5 이하면 제외 (이미 v11에 있는 로직 재사용).
  2. Catalyst 데이터 품질: PDUFA는 비교적 확실하지만 Phase readout은 “언제 발표됐는가”가 noisy. 데이터 가공 과정에서 survivorship bias 주의.
  3. Look-ahead bias: catalyst date가 엔트리 시점에 이미 공시되어 있었어야 실제 trade 가능. 백테스트에 announced_date vs pdufa_date 구분 필요.