# Intraday ORB Reset/Comp Development Workflow

이 문서는 앞으로 `intraday ORB` 전략을 개발할 때 따를 기준이다. 핵심은 신호 알파와 자본 배분을 분리하는 것이다.

## Principle

`daily_budget_reset`은 신호 개발용 truth다. 매일 같은 notional budget으로 평가하기 때문에 후반부 equity 크기가 목적함수를 지배하지 않는다.

`compound_returns`는 운용 검증용 truth다. 실제로 운용할 때 자본이 커지거나 줄어드는 경로 의존성이 반영된다.

따라서 ORB 개발은 다음 규칙을 따른다.

- Entry, ranking, overlay, exit 로직은 먼저 daily reset 전략에서 개발한다.
- Compound 성능이 부족하다고 entry/exit 신호를 comp total return에 맞춰 직접 튜닝하지 않는다.
- Comp 전용 변경은 capital allocator에 한정한다.
- 최종 후보는 reset과 comp를 모두 통과해야 한다.

## Non-Negotiable SQS Policy

`ORB SQS`는 daily reset 전용 rank다. Compound run은 점수를 매기는 대상이 아니라 deployment diagnostic이다.

- `apps.intraday_bt.orb_validate`는 기본적으로 `compound_returns=false`, `daily_budget_reset=true`로 강제해서 SQS를 계산한다.
- `--allow-compound`로 실행한 결과는 SQS rank에 쓰지 않는다. JSON의 score source가 `compound_diagnostic_not_ranked`이면 해당 결과는 운용 검증 메모로만 취급한다.
- Compound 결과가 약하면 entry/ranking/exit를 comp total return에 맞춰 바꾸지 않는다.
- Compound에서 바꿀 수 있는 것은 allocator/capital policy뿐이다: `single_trade_loss_cap_basis`, `single_trade_loss_cap_pct`, `max_position_pct`, `risk_per_trade_pct`, `drawdown_governor_*`, `streak_sizing_*`, `settlement_days`, GFV 관련 정책.
- 특히 계좌가 커진 뒤 sizing이 눌리는 문제는 signal 문제가 아니라 capital policy 문제다. 예를 들어 `single_trade_loss_cap_basis: initial`은 복리 후반부 포지션을 고정 cap으로 누를 수 있고, comp 검증에서는 `equity` basis가 더 적절한지 별도로 본다.

## File Pattern

한 버전은 가능한 한 두 파일로 관리한다.

- `*_reset_*.yaml`: 신호 개발용 primary config.
- `*_comp_allocator.yaml`: 같은 신호를 쓰는 실제 복리 운용 검증용 companion config.

전략 YAML은 항상 standalone이어야 한다. `extends` 기반 상속은 지원하지 않으며, 로더는 `extends` 키를 발견하면 즉시 실패한다. Comp 배포용 파일도 원본 신호 설정을 완전히 materialize한 뒤 아래 capital overlay만 적용한다.

- `compound_returns: true`
- `daily_budget_reset: false`
- `single_trade_loss_cap_basis: equity`

예시:

- [orb_gainers_v49_30_broad_reset_comp_validated.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/strategies/orb_gainers_v49_30_broad_reset_comp_validated.yaml)
- [orb_gainers_v49_30_broad_comp_allocator.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/strategies/orb_gainers_v49_30_broad_comp_allocator.yaml)

## What Can Be Tuned

Daily reset signal layer에서 바꿀 수 있는 것:

- `min_rvol`, `min_breakout_rel_vol`, gap filters, premarket volume filters.
- ranking weights such as `weight_rvol`, `weight_gap`, `weight_premarket_dollar_vol`.
- entry overlays such as crowded-gap, countertrend-gap, stalled-gap, reclaim guards.
- ORB exit behavior such as ATR stop/trail/timeout, if the idea is signal-quality related.

Comp allocator layer에서만 바꿀 수 있는 것:

- `compound_returns`, `daily_budget_reset`, `settlement_days`.
- `max_position_pct`, `risk_per_trade_pct` if used strictly as capital deployment.
- `single_trade_loss_cap_pct`, `single_trade_loss_cap_basis`.
- `drawdown_governor_*`, `streak_sizing_*`, rolling WR sizing.

Comp sweep에서 entry/ranking/exit filter를 같이 바꾸면 안 된다. 그렇게 하면 후반 equity 구간에 맞춘 과최적화가 된다.

## Required Validation Matrix

새 ORB 후보의 기본 검증 기준은 `504 trading days` daily reset이다. `200d`와 `60d`는 최근 구간 훼손 방지용 guard로만 사용한다. 한 구간의 total return만 보고 승격하지 않는다.

기본 검증 커맨드:

```bash
python -m apps.intraday_bt.orb_validate \
  --config configs/intraday/strategies/<candidate_reset_config>.yaml \
  --baseline-config configs/intraday/strategies/<current_baseline_reset_config>.yaml \
  --backend official \
  --start 2024-01-01 \
  --end 2026-05-06 \
  --output-json tmp/orb_validation/<candidate>_vs_<baseline>.json
```

이 커맨드는 아래를 한 번에 계산한다.

- Primary: 최근 `504d` daily reset.
- Chronological split: primary 504d 내부의 `252d train / 126d valid / 126d test`.
- Guard: 최근 `200d`, `60d` daily reset.
- WFV: `252d train / 63d test / 63d step` rolling walk-forward validation.
- Baseline 대비 delta, promotion gate, 단일 `ORB SQS`.

`ORB SQS`는 PEAD의 SQS와 같은 목적으로 쓰는 ORB 전용 점수다. 단일 2년 total return을 직접 최적화하지 않도록 아래 네 축을 합산한 뒤 deployment gate로 감점한다.

- `primary_quality` 25%: 504d return, annualized return, Sharpe, PF, max DD, worst day, tail loss, activity.
- `split_rqs` 30%: 252/126/126 chronological split의 RQS. valid/test가 약하면 점수가 크게 낮아진다.
- `wfqs_v2` 30%: rolling WFV test fold의 수익률, worst fold, positive fold rate, PF, DD, fold variance, trade credibility.
- `temporal_robustness` 15%: 최근 200d에 수익이 과도하게 몰린 후보를 감점한다.

이 점수는 signal quality를 평가한다. Compound return, final equity, 후반부 equity growth는 `ORB SQS` 구성요소가 아니다.

`--backend official`이 승격 기준이다. 이 backend는 [apps/intraday_bt/run.py](/Users/yirugi/mycloud/personal/workspace/fithia2/apps/intraday_bt/run.py)를 직접 호출하므로 live/official backtest 경로와 같은 candidate augmentation, catalyst/attention feature, support ticker, market-quality logic을 사용한다. 실행 시간 때문에 official backend는 504d를 한 번 연속 실행한 뒤 그 일별 결과에서 guard/split/WFV metrics를 파생한다. `--backend research`는 빠른 탐색용으로만 쓴다.

기본 수동 재현 matrix는 아래와 같다.

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/<reset_config>.yaml \
  --days 504
```

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/<reset_config>.yaml \
  --start 2025-07-09 \
  --end 2026-04-23
```

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/<comp_allocator_config>.yaml \
  --days 200
```

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/<comp_allocator_config>.yaml \
  --start 2025-07-09 \
  --end 2026-04-23
```

각 결과는 전략 YAML의 `_meta.description`에 result path, total return, max DD, Sharpe, profit factor, trade count로 기록한다.

## Acceptance Rule

Daily reset은 신호 채택 기준이다.

- 기본 rank는 `ORB SQS`다. 단일 200d/504d return이 높아도 SQS가 낮으면 채택하지 않는다.
- 기존 reset champion보다 `ORB SQS`가 개선되어야 하고, `504d` 기준 total return, DD, Sharpe, profit factor 중 최소 하나가 명확히 개선되어야 한다.
- `200d`와 `60d`는 최근 성능이 크게 훼손되지 않는지 확인하는 guard다. 최근 구간만 좋아지고 `504d`가 망가지면 승격하지 않는다.
- WFV의 test fold가 대부분 양수여야 한다. 단일 fold 또는 최근 1~2개월에만 의존하는 후보는 탈락시킨다.
- DD나 trade count가 악화되면 그 이유가 설명 가능해야 한다.
- 단일 날짜 수익에만 의존하는 후보는 탈락시킨다.
- Sharpe/PF가 비정상적으로 높아진 후보는 slippage stress와 주변 파라미터 안정성까지 통과해야 한다.

Comp는 운용 가능성 기준이다.

- Comp total return이 높아도 reset 성능이 약하면 채택하지 않는다.
- Comp DD가 reset 대비 커지는 것은 정상이나, DD 악화가 수익 개선 대비 과도하면 탈락시킨다.
- Comp 성능 보정은 signal retune이 아니라 allocator sweep으로 처리한다.

## Robustness Gate

Sharpe/PF가 높거나 변경이 소수 trade에만 작동하는 후보는 추가 gate를 통과해야 한다.

- Rolling 200d와 anchored 200d를 모두 검증한다.
- 기본 `slippage_bps=5`뿐 아니라 `10`, `15` bps stress도 같이 본다.
- 하나의 exact parameter가 아니라 주변값 cluster가 같이 살아남아야 한다.
- 정렬 기준은 단일 Sharpe 최고점이 아니라 평균 return, worst return, 평균 PF, worst PF를 같이 본다.
- 좋은 후보가 한 구간 또는 한 slippage에서만 이기면 채택하지 않는다.

예시:

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/orb_gainers_v49_36_broad_reset_late_trade_throttle.yaml \
  --sweep configs/intraday/sweep_orb_v49_37_broad_reset_robustness_gate.yaml \
  --end 2026-04-24
```

```bash
python -u -m apps.intraday_bt.run \
  --config configs/intraday/strategies/orb_gainers_v49_36_broad_reset_late_trade_throttle.yaml \
  --sweep configs/intraday/sweep_orb_v49_37_broad_reset_robustness_gate.yaml \
  --end 2026-04-23
```

V49.37의 `rank_rvol_pressure_size_scale=0.40`은 이 gate에서 rolling/anchored 모두 평균 return/PF 기준으로 가장 안정적이었다. 반대로 `0.60`은 rolling 5bps 단일 조건에서는 좋아 보였지만 anchored와 slippage stress 기준으로는 덜 안정적이므로 채택하지 않는다.

## Sweep Templates

Signal sweep은 reset config를 base로 둔다.

- [sweep_orb_v49_30_broad_reset_signal.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/sweep_orb_v49_30_broad_reset_signal.yaml)

Allocator sweep은 comp allocator config를 base로 두고 capital controls만 포함한다.

- [sweep_orb_v49_30_broad_comp_allocator.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/sweep_orb_v49_30_broad_comp_allocator.yaml)

## V49.30 Baseline

V49.30은 이 workflow를 적용한 첫 baseline이다.

- Reset rolling 200d: `+339.87%`, DD `-1.14%`, Sharpe `5.75`, PF `9.75`, 146 trades.
- Reset anchored 200d: `+341.80%`, DD `-1.73%`, Sharpe `5.73`, PF `9.52`, 142 trades.
- Comp rolling 200d: `+412.23%`, DD `-2.73%`, Sharpe `5.64`, PF `13.93`, 107 trades.
- Comp anchored 200d: `+404.10%`, DD `-2.95%`, Sharpe `5.52`, PF `11.47`, 104 trades.

이후 ORB 개발은 V49.30처럼 reset config를 먼저 만들고, 같은 신호의 comp allocator를 별도 검증하는 방식으로 진행한다.
