# 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/.yaml \ --baseline-config configs/intraday/strategies/.yaml \ --backend official \ --start 2024-01-01 \ --end 2026-05-06 \ --output-json tmp/orb_validation/_vs_.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/.yaml \ --days 504 ``` ```bash python -u -m apps.intraday_bt.run \ --config configs/intraday/strategies/.yaml \ --start 2025-07-09 \ --end 2026-04-23 ``` ```bash python -u -m apps.intraday_bt.run \ --config configs/intraday/strategies/.yaml \ --days 200 ``` ```bash python -u -m apps.intraday_bt.run \ --config configs/intraday/strategies/.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를 별도 검증하는 방식으로 진행한다.