# ORB Gainers Strategy Development And Validation 이 문서는 현재 ORB 전략 중 `gainers_leader` 계열을 어떻게 개발하고 검증하는지 정리한 운영 문서다. 목표는 단순하다. - `Yahoo Finance Top Gainers`처럼 당일 비정상 강세 종목을 시스템적으로 잡는다. - 장 초반 ORB 구조를 쓰되, 실제 edge는 `압축 후 확장`이 아니라 `same-day attention / catalyst / abnormal volume`에서 찾는다. - 연구 helper 결과와 공식 `run.py` 결과가 항상 같도록 검증한다. 이 문서는 “어떤 아이디어를 어떻게 실험하고”, “무엇을 official truth로 보고”, “어떤 함정을 피해야 하는지”를 남들이 그대로 따라할 수 있게 쓰였다. ## 1. 전략 의도 현재 `ORB Gainers`의 철학은 아래와 같다. 1. 당일 강한 종목을 찾는다. - 단순 compression setup보다 `당일 갭`, `초반 거래량`, `프리마켓 거래대금`이 중요하다. 2. 09:35 이후의 ORB 구조를 이용해 진입한다. - 첫 5분 봉 방향과 breakout 구조는 유지한다. 3. 같은 날 관심이 몰린 종목만 남긴다. - 명시적 뉴스 데이터가 없으므로 `premarket dollar volume`을 `same-day catalyst proxy`로 쓴다. 4. 검증은 `2026 Q1`을 1차 test window로 본다. - 이 기간은 최근 구조를 반영하면서도 30일보다 충분히 길다. ## 2. 현재 공식 전략 공식 전략 파일: - [orb_gainers.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/strategies/orb_gainers.yaml) 현재 공식 파라미터의 핵심: - `engine_family: gainers_leader` - `entry_direction: long_only` - `orb_minutes: 5` - `sim_bar_minutes: 5` - `order_timeout_minutes: 45` - `min_price: 10` - `min_avg_dollar_volume: 25,000,000` - `min_atr_14: 0.50` - `min_rvol: 1.2` - `min_abs_gap_pct: 0.02` - `min_premarket_dollar_vol: 1,500,000` - `max_gap_pct: null` - `max_candidates: 12` - `max_candidates_per_sector: 2` - `min_candidates_to_trade: 1` - `weight_rvol/gap/dollar_vol/premarket = 0.40 / 0.20 / 0.05 / 0.35` - `atr_stop_multiplier: 0.5` - `breakeven_at_r: 1.0` - `trailing_at_r: 3.0` - `trailing_stop_atr_multiplier: 0.3` - `compound_returns: false` 왜 이렇게 바뀌었는가: - 예전 `compression_breakout`은 원래 의도와 맞지 않았다. - `0.10 ATR` stop은 너무 타이트해서 거의 바로 stop-out 됐다. - `premarket dollar volume`을 강하게 쓰는 것이 Q1에서 가장 잘 작동했다. ## 3. Source Of Truth ORB 개발에서 source of truth는 아래 순서다. 1. 공식 전략 config - [orb_gainers.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/strategies/orb_gainers.yaml) 2. 공식 단일 백테스트 실행기 - [apps/intraday_bt/run.py](/Users/yirugi/mycloud/personal/workspace/fithia2/apps/intraday_bt/run.py) 3. 연구 helper / lab - [apps/intraday_bt/orb_research.py](/Users/yirugi/mycloud/personal/workspace/fithia2/apps/intraday_bt/orb_research.py) - [apps/intraday_bt/lab.py](/Users/yirugi/mycloud/personal/workspace/fithia2/apps/intraday_bt/lab.py) 4. 실험 결과물 - `runs/intraday_orb/...` 중요: - helper/lab은 빠른 연구용이다. - 최종 성과와 전략 등록 판단은 반드시 `apps.intraday_bt.run`으로 다시 확인한다. ## 4. 현재 검증 기준 ### Primary Test 현재 primary test window는: - `2026-01-02 ~ 2026-03-31` 이 기간은 Q1 구간이며, 현재 ORB gainers 전략 개선은 이 구간을 기준으로 한다. ### Secondary Sanity Check 추가 sanity check는: - `--days 30` 최근 30거래일 이유: - Q1만 좋고 최근 30일에서 activity가 0이면 전략이 죽은 상태일 수 있다. - 반대로 최근 30일만 좋고 Q1 전체가 약하면 과적합 가능성이 높다. ## 5. 현재 공식 성과 공식 `run.py` 기준 최신 결과: ### Q1 공식 검증 - 결과 파일: [intraday_20260415_204341_9d8e4fee.json](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/intraday_orb/intraday_20260415_204341_9d8e4fee.json) - 기간: `2026-01-02 ~ 2026-03-31` - 결과: - `+6.96%` - `407 trades` - `Sharpe 2.05` - `Max drawdown -3.92%` ### 최근 30일 공식 검증 - 결과 파일: [intraday_20260415_204456_60bdfe03.json](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/intraday_orb/intraday_20260415_204456_60bdfe03.json) - 기간: `2026-03-04 ~ 2026-04-15` - 결과: - `+4.05%` - `211 trades` - `Sharpe 2.15` - `30/30 days with trades` ## 6. 개발 루프 현재 ORB gainers 개발은 아래 순서로 한다. ### Step 1. 아이디어를 전략 언어로 번역 질문: - “당일 리더를 찾는가?” - “뉴스/촉매 proxy가 들어갔는가?” - “큰 갭을 오히려 막고 있지 않은가?” - “activity를 죽이는 규칙이 없는가?” 현재 유효했던 아이디어: - `premarket dollar volume`을 `same-day catalyst proxy`로 사용 - `gap + rvol + premarket attention` 랭킹 - `0.5 ATR stop + 3R ATR trail` ### Step 2. Helper로 좁은 실험 빠른 실험은 `orb_research.py`의 research snapshot / tape cache를 재사용한다. 핵심은: - full raw intraday를 매번 다시 읽지 않는다. - 같은 Q1 구간을 여러 파라미터로 빠르게 비교한다. 최근 Q1 비교 결과 파일: - [focus2](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/intraday_orb/lab/quick_real_202404_202603_gainers_q1_focus2.json) - [focus3](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/intraday_orb/lab/quick_real_202404_202603_gainers_q1_focus3.json) - [focus4](/Users/yirugi/mycloud/personal/workspace/fithia2/runs/intraday_orb/lab/quick_real_202404_202603_gainers_q1_focus4.json) 이 단계의 목적은: - 최고의 parameter cluster를 빠르게 찾는 것 - official result를 확정하는 것이 아님 ### Step 3. Official `run.py` 재검증 helper에서 좋아 보여도 반드시 `run.py`로 다시 확인한다. 예시: ```bash .venv/bin/python3 -m apps.intraday_bt.run \ --config configs/intraday/strategies/orb_gainers.yaml \ --start 2026-01-02 \ --end 2026-03-31 ``` 최근 30일 sanity: ```bash .venv/bin/python3 -m apps.intraday_bt.run \ --config configs/intraday/strategies/orb_gainers.yaml \ --days 30 ``` ### Step 4. 결과가 맞으면 config 업데이트 official result까지 확인된 뒤에만 전략 파일을 바꾼다. ## 7. 꼭 체크해야 할 함정 ### 7.1 Helper와 `run.py` 결과가 다를 수 있다 실제로 한 번 발생했다. 원인: - `run.py` 경로가 `ticker_sectors`를 ORB simulator에 넘기지 않아서 `max_candidates_per_sector`가 공식 백테스트에서 무시되고 있었다. - helper는 sector cap을 적용하고, official run은 적용하지 않아 결과가 크게 어긋났다. 수정 위치: - [apps/intraday_bt/run.py](/Users/yirugi/mycloud/personal/workspace/fithia2/apps/intraday_bt/run.py) 원칙: - helper에서 좋은 결과가 나오면 official run으로 trade count부터 먼저 맞춰본다. ### 7.2 `min_candidates_to_trade`는 activity를 쉽게 0으로 만든다 예전 compression 전략이 최근 30일 `0 trades`였던 직접 원인 중 하나다. 원칙: - gainers 계열에서는 기본적으로 `min_candidates_to_trade = 1`부터 시작한다. ### 7.3 `max_gap_pct`는 gainers 전략과 상충할 수 있다 top gainers 전략은 본질적으로 큰 움직임을 잡는 전략이다. 원칙: - gainers 계열에서는 `max_gap_pct`를 보통 `null`로 두고, - 대신 `min_abs_gap_pct`와 `min_premarket_dollar_vol`로 품질을 걸러낸다. ### 7.4 `0.10 ATR` stop은 지나치게 타이트할 수 있다 실제 Q1 비교에서: - `0.10 ATR`은 매우 나빴다. - `0.50 ATR` 쪽이 명확히 우세했다. 원칙: - gainers 계열은 `0.10 ATR`을 baseline으로 두지 않는다. ## 8. 어떤 축을 우선 튜닝할 것인가 가장 효율적인 튜닝 순서: 1. `min_premarket_dollar_vol` 2. ranking weights - `weight_rvol` - `weight_gap` - `weight_premarket_dollar_vol` 3. `atr_stop_multiplier` 4. `trailing_at_r` 5. `trailing_stop_atr_multiplier` 6. `max_candidates` 7. `max_candidates_per_sector` 현재까지는: - `premarket dollar volume`이 가장 큰 설명력을 보였다. ## 9. Cache / Snapshot 구조 ORB 개발 속도를 위해 아래 캐시를 사용한다. - raw daily cache: [data/cache/daily](/Users/yirugi/mycloud/personal/workspace/fithia2/data/cache/daily) - raw intraday cache: [data/cache/intraday](/Users/yirugi/mycloud/personal/workspace/fithia2/data/cache/intraday) - research snapshot: [data/cache/orb_research](/Users/yirugi/mycloud/personal/workspace/fithia2/data/cache/orb_research) - period metrics cache: [data/cache/orb_eval](/Users/yirugi/mycloud/personal/workspace/fithia2/data/cache/orb_eval) - prepared tape cache: [data/cache/orb_tape](/Users/yirugi/mycloud/personal/workspace/fithia2/data/cache/orb_tape) 운영 원칙: - helper 실험은 snapshot/tape를 적극 활용한다. - official truth는 여전히 `run.py` 결과다. ## 10. 권장 검증 명령 ### 코드/회귀 검증 ```bash python -m py_compile \ apps/intraday_bt/run.py \ apps/intraday_bt/lab.py \ libs/intraday/orb_simulator.py pytest -q \ tests/unit/intraday/test_run_helpers.py \ tests/unit/intraday/test_orb_lab.py \ tests/unit/intraday/test_orb_simulator.py ``` ### Q1 공식 백테스트 ```bash .venv/bin/python3 -m apps.intraday_bt.run \ --config configs/intraday/strategies/orb_gainers.yaml \ --start 2026-01-02 \ --end 2026-03-31 ``` ### 최근 30일 sanity ```bash .venv/bin/python3 -m apps.intraday_bt.run \ --config configs/intraday/strategies/orb_gainers.yaml \ --days 30 ``` ### Quick lab ```bash python -u -m apps.intraday_bt.lab \ --config configs/intraday/strategies/orb_gainers.yaml \ --quick \ --beam-width 2 \ --output-dir runs/intraday_orb/lab/quick_orb_gainers ``` 주의: - lab 결과가 좋으면 반드시 `run.py`로 다시 확인한다. ## 11. 다음 개발자가 해야 할 일 다음 사람이 이어받으면 아래 순서로 하면 된다. 1. 공식 config를 읽는다. - [orb_gainers.yaml](/Users/yirugi/mycloud/personal/workspace/fithia2/configs/intraday/strategies/orb_gainers.yaml) 2. Q1 official run을 먼저 돌린다. 3. 최근 30일 sanity를 확인한다. 4. 개선 아이디어는 먼저 helper로 좁게 비교한다. 5. helper winner를 official `run.py`로 검증한다. 6. trade count / return / sharpe가 helper와 공식 run에서 비슷한지 확인한다. 7. 일치하면 config를 업데이트한다. ## 12. 현재 상태 요약 현재 결론은 이렇다. - `compression`보다 `gainers_leader`가 전략 의도에 맞다. - `premarket dollar volume`은 매우 중요한 품질 필터다. - `0.5 ATR stop + 3R ATR trail`이 지금까지는 가장 실전적이다. - helper와 official run 차이는 반드시 의심하고 검증해야 한다. - 현재 공식 `ORB Gainers`는: - Q1 `+6.96%` - 최근 30일 `+4.05%` - activity도 충분히 나온다.