# ORB Gainers V23 전략 완전 해설

**버전:** V23 (id: 28)  
**상태:** DEFINITIVE FINAL CHAMPION — 모든 개선 축 소진, 라이브 배포 중  
**배포일:** 2026-04-20  
**백테스트 기준일:** 200d 2025-07-XX ~ 2026-04-XX, 400d 2024-09-XX ~ 2026-04-XX

---

## 1. 전략 철학

ORB Gainers의 핵심은 **당일 비정상 관심 종목의 장 초반 돌파**를 잡는 것이다.

Yahoo Finance Top Gainers처럼 그날 강하게 움직이는 종목을 시스템으로 추린다. 진입 구조는 Opening Range Breakout(ORB) — 첫 5분봉의 고점 돌파 — 이지만, 실제 엣지의 원천은 **압축 후 확장**이 아니라 **당일 attention / catalyst / 비정상 거래량**이다.

핵심 철학:
- 당일 갭 + 프리마켓 거래대금 + 상대 거래량 = catalyst proxy
- ATR이 높은 종목만 선별해 폭발적 후속 움직임이 있는 후보만 취급
- daily_budget_reset + compound_returns=false: 단리 방식으로 매일 초기자본($10k) 기준 sizing

---

## 2. V23 개발 계보 요약

V23은 V1부터 22번의 반복 개선 끝에 도달한 최종 버전이다.

| 버전 | 핵심 발견 | 200d 성과 |
|------|-----------|-----------|
| V1-V3 | 초기 ORB 구조 실험 | -19% (regime 미적용) |
| V4-V7 | rolling loss filter, QQQ regime, min_candidate_breadth | +2~9% |
| V8 | weight_momentum=0.15 + QQQ threshold=0.0 | +19.73% |
| V9 | min_rvol=1.5 (momentum과 시너지) | +23.50% (400d) |
| V10 | trailing_at_r: 1.5→1.0 (더 일찍 트레일) | +33.17% (400d) |
| V11 | min_breakout_rel_vol=1.2 (돌파 거래량 확인) | +36.19% (400d) |
| V15 | streak_sizing + drawdown_governor + compound | +41.60% (200d) |
| V16 | max_gap_pct=0.10, simul=4, risk=0.035 | +71.01% (200d) |
| V17 | max_gap_pct=0.04, no_partial_exit, risk=0.04 | +84.36% (200d) |
| V18 | daily_reset 모드 전환, QQQ +0.15% filter, maxpos=0.80 | +103.50% (200d DR) |
| V19 | streak_win_bonus: 0.30→0.60 | +85.56% WR 60.2% |
| V21 | trailing_tighten_at_r: 2.5→2.0 | +97.25% (200d) |
| V22 | gov_threshold: 0.03→0.025, streak_bonus: 0.60→0.70 | +98.73% (200d) |
| **V23** | **min_atr_pct=0.04, maxpos: 0.80→0.70** | **+109.32% (200d 확정)** |

**V24-V32**: 9회 연속 실패 — V23이 daily_reset 국소 최적점임을 확정.

---

## 3. V23 핵심 변경사항 (from V22)

### 변경 1: `min_atr_pct: 0.04`

ATR-14 ÷ prev_close ≥ 4% 이상인 종목만 후보로 허용.

**근거**: 400d 거래 데이터 분석
- ATR/price 2-3%: WR 42.3%, 총 P&L 기여 -8%
- ATR/price 3-4%: WR 46.2%, 총 P&L 기여 -12%
- ATR/price 4-5%: WR 57.8%
- ATR/price >5%: **WR 62.4%, 총 P&L 기여 +104%**

에너지주/안정적 기술주처럼 변동성이 낮은 종목은 ORB 전략이 요구하는 **돌파 후 후속 추진력**이 없다. 이들을 걸러내면 WR이 크게 개선된다.

**효과**: 200d 수익률 +18.19pp, WR +0.1pp, DD 거의 동일

### 변경 2: `max_position_pct: 0.80 → 0.70`

ATR이 높은 종목은 개별 거래 변동성이 크다. 포지션 한도를 낮춰 포트폴리오 수준 DD를 억제.

**검증**:
- max_position_pct=0.75 + min_atr_pct=0.04: 400d DD -24.32% (**FAIL** 게이트 기준 -24% 초과)
- max_position_pct=0.70 + min_atr_pct=0.04: 400d DD -23.97% (**PASS** by 0.03pp)

### 두 변경의 시너지

min_atr_pct 단독: 400d DD -24.38% (게이트 실패)  
max_position_pct 단독: 효과 미미  
**함께**: 고품질 후보 선별 + 변동성 관리 → 시너지

---

## 4. 전략 파라미터 전체

```yaml
strategy_mode: orb

orb_strategy:
  # === 엔진/기본 ===
  engine_family: gainers_leader     # 당일 강세주 추적 엔진
  live_readiness: live_ready
  entry_direction: long_only
  orb_minutes: 5                    # 9:30-9:35 첫 5분봉
  sim_bar_minutes: 5
  order_timeout_minutes: 45         # 9:40-10:25 진입 대기

  # === 진입 허용 규칙 ===
  allow_doji_breakout: true         # 도지봉도 돌파 허용
  allow_red_to_green_breakout: true # 갭하 후 반등도 허용

  # === 후보 필터 ===
  min_price: 10.0                   # $10 이상
  min_avg_dollar_volume: 25000000   # 평균 거래대금 $25M 이상
  min_atr_14: 0.50                  # ATR-14 절대값 $0.50 이상
  min_atr_pct: 0.04                 # ★ V23: ATR/price ≥ 4% (변동성 확인)
  min_rvol: 1.5                     # 상대 거래량 1.5배 이상
  min_abs_gap_pct: 0.02             # 갭 2% 이상
  min_premarket_dollar_vol: 1500000 # 프리마켓 거래대금 $1.5M 이상
  max_gap_pct: 0.04                 # 갭 상한 4% (V17에서 도입)
  max_candidates: 20                # 후보 최대 20종목
  max_candidates_per_sector: 3      # 섹터당 최대 3종목
  min_candidates_to_trade: 1        # 최소 1종목 있으면 거래

  # === 시장 레짐 필터 ===
  min_candidate_breadth: 0.60       # 후보 60%가 양봉 돌파 조건 충족 시만 거래
  market_regime_spy_threshold: 0.0015
  market_regime_ticker: QQQ         # QQQ 기준 레짐 판단
  # QQQ 오픈 갭 > +0.15% → 거래 / ≤ 0.15% → 스킵 (V18에서 도입)

  # === 롤링 손실 제한 ===
  rolling_loss_days: 7              # 최근 7거래일
  rolling_loss_threshold: -0.07     # 7일 누적 손실 -7% 이하 시 거래 중단
  ticker_cooldown_days: 0           # 종목 재사용 쿨다운 없음

  # === 포지션 관리 ===
  max_simultaneous_entries: 3       # 동시 최대 3종목 진입
  min_breakout_rel_vol: 1.2         # 돌파봉 거래량 ≥ ORB 이후 평균 1.2배 (V11에서 도입)

  # === 후보 랭킹 가중치 ===
  weight_rvol: 0.35                 # 상대 거래량 35%
  weight_gap: 0.20                  # 갭 크기 20%
  weight_dollar_vol: 0.05           # 당일 거래대금 5%
  weight_premarket_dollar_vol: 0.25 # 프리마켓 거래대금 25%
  weight_body_ratio: 0.0            # 캔들 몸통 비율 미사용
  weight_momentum: 0.15             # 5일 모멘텀 15% (V8에서 도입)

  # === 리스크 관리 / 스탑 ===
  atr_stop_multiplier: 0.75         # 진입 후 스탑: 진입가 - 0.75×ATR
  breakeven_at_r: 1.0               # 1R 도달 시 본전 스탑으로 이동
  trailing_at_r: 1.0                # 1R 도달 시 트레일링 활성화 (V10에서 도입)
  trailing_stop_atr_multiplier: 0.8 # 트레일링 스탑: 고점 - 0.8×ATR
  trailing_tighten_at_r: 2.0        # 2R 도달 시 트레일 조임 (V21에서 2.5→2.0)
  trailing_stop_atr_multiplier_tight: 0.3  # 조인 트레일: 고점 - 0.3×ATR
  partial_exit_at_r: 99.0           # 사실상 부분 청산 없음 (V17에서 제거)
  partial_exit_pct: 0.50

  # === 사이징 ===
  risk_per_trade_pct: 0.05          # 거래당 리스크 5%
  max_position_pct: 0.70            # ★ V23: 일일 예산 대비 최대 포지션 70%
  daily_max_loss_pct: 0.05          # 일별 최대 손실 5%
  max_stops_per_day: 5              # 하루 최대 스탑아웃 5회
  exit_minutes_before_close: 5      # 장 마감 5분 전 강제 청산

  # === drawdown 거버너 ===
  drawdown_governor_threshold: 0.025  # 피크 대비 -2.5% 이상 DD 시 사이징 축소 시작 (V22에서 0.03→0.025)
  drawdown_governor_min_scale: 0.30   # DD 시 최소 30%까지 사이징 축소

  # === 스트릭 사이징 ===
  streak_sizing_win_bonus: 0.70       # 연승 시 70%씩 사이징 증가 (V22에서 0.60→0.70)
  streak_sizing_max: 2.5              # 최대 2.5배까지

  # === 실행 모드 ===
  slippage_bps: 5.0           # 백테스트 슬리피지 5bp
  initial_capital: 10000      # 초기 자본 $10,000
  compound_returns: false      # 단리 모드
  daily_budget_reset: true     # 매일 $10k 기준으로 사이징 리셋
  settlement_days: 1           # 백테스트: T+1 결제 (라이브에선 0으로 오버라이드)

universe:
  source: midlarge             # mid+large cap 유니버스 (S&P400 + S&P500)
```

---

## 5. 후보 선별 메커니즘

### 5.1 프리마켓 스크리닝 (09:20 ET)

1. **유니버스 로드**: midlarge universe (S&P 400 + S&P 500)
2. **일봉 데이터 수집**: Oracle `/api/v1/price/data` 경유
3. **기본 필터**:
   - min_price ≥ $10
   - min_avg_dollar_volume ≥ $25M
   - min_atr_14 ≥ $0.50
   - **min_atr_pct ≥ 4%** (V23 신규)
4. **프리마켓 데이터**:
   - Oracle snapshots API로 프리마켓 갭, 거래대금 수집
   - min_abs_gap_pct ≥ 2%, max_gap_pct ≤ 4%
   - min_premarket_dollar_vol ≥ $1.5M
5. **랭킹**: rvol(0.35) + gap(0.20) + dollar_vol(0.05) + premkt_dollar_vol(0.25) + momentum(0.15) 가중합
6. **상위 20종목**, 섹터당 최대 3종목

### 5.2 ORB 감지 (09:40 ET)

1. **인트라데이 9:30-9:35 5분봉** 수집 (Oracle 경유)
2. **ORB 레벨 확정**: 첫 5분봉 고가(breakout level)
3. **레짐 체크**:
   - QQQ 오픈 갭 > 0.15% 이어야 거래
   - 후보 중 60% 이상이 양봉 조건 충족해야 거래
4. **롤링 손실 체크**: 최근 7일 누적 -7% 초과 시 거래 스킵

### 5.3 돌파 감지 (09:40~10:25, 5분 간격)

- 현재가 > ORB 고가 → 진입 트리거
- **min_breakout_rel_vol ≥ 1.2**: 돌파봉 거래량 ≥ ORB 이후 평균 거래량 × 1.2배
- 동시 최대 3종목
- 45분 이내 미진입 → 자동 만료

---

## 6. 포지션 관리 메커니즘

### 스탑 로직 (orb_simulator.py:477-580 미러링)

```
진입 후:
  초기 스탑 = 진입가 - 0.75×ATR

1R 도달 시 (수익 = 리스크×1):
  스탑 → 본전 (breakeven_at_r=1.0)
  트레일링 활성화

트레일링 중:
  스탑 = max(현재 스탑, 최고가 - 0.8×ATR)

2R 도달 시 (trailing_tighten_at_r=2.0):
  스탑 = max(현재 스탑, 최고가 - 0.3×ATR)  ← 더 빠듯하게 조임

강제 청산: 15:55 ET
```

### 사이징

```python
# daily_budget_reset=true: 매일 initial_equity($10k) 기준
daily_budget = 10,000  # (compound_returns=false, 단리)

# 기본 사이징
risk_amount = daily_budget × risk_per_trade_pct(0.05) = $500
position_size = risk_amount / (ATR × atr_stop_multiplier)
max_dollars = daily_budget × max_position_pct(0.70) = $7,000
actual_position = min(position_size, max_dollars)

# drawdown 거버너 조정
# DD > 2.5% 시 0.30~1.0 스케일 적용

# 스트릭 사이징 조정
# 연승 시 +70%씩 증가, 최대 2.5배
```

---

## 7. 검증 결과

### TRUE BASELINE (확정, 2026-04-20)

200d 백테스트 (`intraday_20260420_203705_99ded639.json` + 재확인 `5597b16d`):

| 지표 | 수치 |
|------|------|
| **수익률** | **+109.32%** |
| **승률 (WR)** | **58.1%** |
| **Sharpe** | **3.01** |
| **최대 DD** | **-12.91%** |
| **거래 수** | **160건** |

> 참고: 이전 기록 +116.92%는 데이터 파이프라인 버그(AH 클로즈 포함) 결과임. 정식 기준치는 +109.32%.

### 400d 검증

| 지표 | V22 | V23 | 게이트 |
|------|-----|-----|--------|
| 수익률 | +89.62% | **+120.87%** | ≥88% ✓ |
| WR | 52.2% | **57.7%** | ≥52% ✓ |
| DD | -20.93% | -23.97% | ≥-24% ✓ (0.03pp 마진) |
| Sharpe | 1.67 | **1.94** | |
| 거래 수 | 312건 | 286건 | |

### 분기별 400d 성과 분포

| 기간 | 수익률 |
|------|--------|
| 2024-Q3 | -4.4% |
| 2024-Q4 | +1.2% |
| 2025-Q1 | -8.4% |
| 2025-Q2 | +13.0% |
| 2025-Q3 | **+42.4%** |
| 2025-Q4 | +11.0% |
| 2026-Q1 | **+35.2%** |

2024-Q3 및 2025-Q1 손실은 변동성 높은 매크로 환경에서 발생. 전략이 2024-2026 AI/crypto/biotech 모멘텀 레짐에 최적화되어 있음.

---

## 8. 리스크 관리

### 8.1 다층 리스크 필터

| 단계 | 필터 | 용도 |
|------|------|------|
| 종목 선별 | min_atr_pct ≥ 4% | 저변동성 drag 종목 제거 |
| 시장 레짐 | QQQ 갭 > 0.15% | 플랫/하락장 스킵 |
| 후보 품질 | breadth ≥ 60% | 분산된 강세 확인 |
| 롤링 손실 | 7일 -7% | 연속 손실 후 거래 중단 |
| 돌파 확인 | breakout_rel_vol ≥ 1.2 | 허위 돌파 제거 |
| 거버너 | DD > 2.5% → 사이징 축소 | 자본 보호 |
| 회로 차단기 | DD > 25% → 세션 정지 | 극단적 손실 방지 |

### 8.2 계좌 수준 회로 차단기

`apps/orb_trader/engine.py:46`:
```python
_ACCOUNT_CIRCUIT_BREAKER_PCT = 25.0
```
피크 대비 25% 이상 DD 시 해당 세션의 모든 거래 정지.

### 8.3 알려진 한계 (CRITICAL)

**600d 백테스트**: +31.16%, DD **-51.25%**

V23은 2024-2026 고ATR 모멘텀 레짐(AI/crypto/biotech)에 맞춰 최적화됨. 2023년 후반 이전 기간을 포함하면 DD가 -51%로 급증. 시장 레짐이 바뀌면 전략 성과가 크게 저하될 수 있음.

**레짐 변화 신호 모니터링 권장**:
- 7일 롤링 WR이 40% 미만으로 하락하면 전략 효능 의심
- 연속 3일 이상 "no candidates" 발생 시 레짐 체크

---

## 9. 라이브 배포

### 인프라

| 항목 | 값 |
|------|-----|
| DB | `data/paper/orb.db` |
| 세션 ID | `e492695e` |
| 세션명 | V23 ORB Gainers |
| 초기 자본 | $10,000 |
| 상태 파일 | `data/paper/.orb_auto_state.json` |
| 데몬 | `apps/orb_trader/daemon.py` |

### 일별 스케줄 (ET)

| 시간 | 동작 |
|------|------|
| 09:20 AM | `run_pre_screen()` — 일봉 + 프리마켓 스크리닝 |
| 09:30-09:35 AM | ORB 윈도우 (5분봉 형성) |
| 09:40 AM | `run_orb_detection()` — 인트라데이 바 → 후보 확정 |
| 09:40-10:25 AM | `run_breakout_check()` 매 5분 — 돌파 감지 + 진입 |
| 09:40-15:55 PM | `run_stop_check()` 매 5분 — 스탑/트레일링 체크 |
| 03:55 PM | `run_eod_exit()` — 전체 포지션 청산 |
| 04:00 PM | `run_post_close()` — 일별 스냅샷 기록 |

### Oracle 의존성 (CRITICAL)

**Oracle 서버** (`stock-oracle` 프로젝트, 로컬 `localhost:18001`)가 **09:20 ET 이전에 반드시 실행 중**이어야 한다.

- `AlpacaBroker.get_bars()` → Oracle `/api/v1/price/data`
- `AlpacaBroker.get_intraday_bars()` → Oracle
- Oracle 없이: 바 데이터 수집 실패 → 후보 0개 → 거래 없음
- **Alpaca SDK 폴백 없음** — Oracle이 유일한 데이터 경로

`run_pre_screen()` 시작 시 Oracle 헬스 체크 자동 실행 (3초 타임아웃). 미응답 시 로그에 CRITICAL 경고.

---

## 10. 소진된 개선 축 (참고용)

V23이 local optimum임을 보여주는 실패 기록. 이 축들은 더 이상 탐색할 필요 없음.

### 파라미터 스윕 (V24, 17개 변형)
- trailing/risk/governor/streak/atr_stop/rvol 조정 → 전부 -2pp 이상 열화
- min_atr_pct 0.05: 과필터 (+79%)
- min_atr_pct 0.03: 불충분 (+88%)

### 유니버스 변형
| 유니버스 | 200d | 판정 |
|----------|------|------|
| midlarge (현재) | +109.32% | CHAMPION |
| largecap | +28.1% | FAIL |
| midcap | +33.3% | FAIL |
| smallmid | +2.1% | FAIL |
| sp500 | +1.3% | FAIL |

### 레짐 필터 추가 시도 (V24 regime kill-switch)
- QQQ 50d/20d 트렌드 필터
- VIX max 25
- rolling 30일 -7%/-5% 손실 필터
- 결론: 2024년 나쁜 날들은 어떤 관측 가능한 신호로도 미리 구분 불가. 필터 추가 무의미.

### ORB 타임프레임
- 10분봉: -18.87% (재앙)
- 15분봉: -11.14% (재앙)
→ 5분봉이 유일한 최적점

### 기타 전부 실패
- compound 모드: DD -24.74% (게이트 실패)
- VWAP 확인/청산: 중립 또는 마이너스
- 진입바 클로즈 진입: -22.80pp (재앙)
- 부분 청산 1.5R/50%: 마이너스 (V17에서 제거)

---

## 11. 실행 명령어

### 백테스트

```bash
# 200d 공식 백테스트
.venv/bin/python3 -m apps.intraday_bt.run \
  --config configs/intraday/strategies/orb_gainers_v23.yaml \
  --days 200

# 최근 30일 sanity check
.venv/bin/python3 -m apps.intraday_bt.run \
  --config configs/intraday/strategies/orb_gainers_v23.yaml \
  --days 30

# 400d 검증
.venv/bin/python3 -m apps.intraday_bt.run \
  --config configs/intraday/strategies/orb_gainers_v23.yaml \
  --days 400
```

### 라이브 상태 확인

```bash
# 데몬 상태 확인
python -m apps.web.orb_trading_service status

# 최근 로그
tail -50 data/paper/orb_daemon.log

# DB 조회 — 오늘 거래
python -c "
import sqlite3
conn = sqlite3.connect('data/paper/orb.db')
rows = conn.execute('SELECT * FROM trades ORDER BY entry_time DESC LIMIT 20').fetchall()
for r in rows: print(r)
"
```

### 코드 검증

```bash
python -m py_compile \
  apps/intraday_bt/run.py \
  libs/intraday/orb_simulator.py \
  apps/orb_trader/engine.py

pytest -q tests/unit/intraday/
```

---

## 12. 다음 단계 (미래 연구)

V23은 현재 아키텍처 내의 최종 버전이다. 추가 개선은 **근본적으로 다른 전략 아키텍처**를 필요로 한다:

- VWAP deviation 전략 (개장 후 VWAP 이탈 포착)
- Opening Range Reversal (ORB 실패 후 반전)
- 뉴스 데이터 통합 (현재는 프리마켓 거래대금으로 proxy 사용)
- 멀티 타임프레임 확인 (1분봉 엔트리 + 5분봉 필터)

이들은 V23의 파라미터 튜닝이 아닌 새 연구 트랙으로 진행해야 한다.
