fix(정합성): 틱 lag wall-clock 정합 + 3벤더 DB 저장 스위치 통일
- feed_fallback.bar_is_garbage: 봉끝 기준 → 각 틱의 recv_ts wall-clock 기준으로 정정 실매 RAM 3초컷과 동일 논리 → 유동성 낮은 종목 부당 스킵 해소 - candle_garbage_fallback_enabled: 기본 True 복원 (wall-clock 정정 후 안전) - param_search_optuna·run_tail_backtest_cli: CANDLE_GARBAGE_FALLBACK·BACKTEST_USE_RUST 강제 os.environ 세팅 제거 → DB env·CLI 플래그로만 관리 (UI 존중) - WS_TICK_DB_SAVE_LAG_CUT_ENABLED 신설 (bool, 기본 false, 3벤더 공통) OFF=키움/KIS/LS 모든 틱 lag 무관 전부 저장 (벤더 통계·재현·백테 정합) ON=lag > LIVE_FEED_FALLBACK_MAX_AGE_SEC 이면 미저장 (미래 A안) - KIWOOM_TICK_LIVE_MAX_LAG_SEC 완전 폐기 → 위 스위치로 통일 - kiwoom_ws: _skip_persist 로직 새 스위치로 교체 - kis_ws·ls_ws: _skip_persist_kis/_ls 신규 (벤더별 상이했던 정책 통일) - docs/정합성.md §9 신설 (문제·결정·시나리오·향후 A안 전환법) - docs/rust_engine_parity_port_plan.md (신규 설계) 실매 스모크: logs/test_live_execution_validation_20260906_191845.log → 최종: 통과 · 👑 완결 브라우저 검증: http://192.168.0.149:5050/#liveconfig → 새 스위치 노출, JS 오류 없음 영향: 실매(DB 저장 정책 통일, RAM 컷 변경 없음) + 백테/Optuna(실매 정합) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
76
docs/정합성.md
76
docs/정합성.md
@@ -198,3 +198,79 @@ freeze가 안정된 **뒤에만**. 지금은 필수 아님.
|
||||
|
||||
- 신호/진입을 ±1분으로 땜빵하지 않는다
|
||||
- freeze를 끄거나 `*_SKIP_HTS_SCAN_DUPES`를 true로 바꾸지 않는다
|
||||
|
||||
---
|
||||
|
||||
## 9. 틱 lag 정합 (2026-09-06 · 실매 RAM ↔ 백테 wall-clock 일치)
|
||||
|
||||
이전 사고: 옵투나에서 **12~22% 봉이 "쓰레기"** 로 스킵. 원인은 3가지가 겹침.
|
||||
|
||||
### 9.1 문제 (근본원인 요약)
|
||||
|
||||
| # | 코드 | 문제 |
|
||||
|---|------|------|
|
||||
| 1 | `bar_is_garbage` 가 **봉끝 시각(bar_end)** 을 기준으로 `lag = bar_end - tick_time` | 실매 RAM은 `wall-clock - tick_time` 로 판정 → 백테는 봉 시작 근처 유동성 낮은 종목 **부당 스킵** |
|
||||
| 2 | `param_search_optuna.py` 가 import 시점에 `CANDLE_GARBAGE_FALLBACK=False` 강제 세팅 | 사용자가 UI에서 켜도 무시 |
|
||||
| 3 | 벤더별 DB 저장 정책 제각각 (kiwoom: `KIWOOM_TICK_LIVE_MAX_LAG_SEC>0` 이면 컷 · kis: 무조건 저장 · ls: RAM skip 시 저장 안 함) | 통계·재현·백테 정합이 벤더마다 다름 |
|
||||
|
||||
### 9.2 결정 (사용자 승인)
|
||||
|
||||
1. **`bar_is_garbage` = wall-clock 정합** — 각 틱의 `recv_ts` 를 기준으로 `lag = recv_ts - tick_time` 계산.
|
||||
→ 실매 RAM 3초컷과 **동일 논리**. 유동성 낮은 종목 부당 스킵 해소.
|
||||
2. **DB 는 전부 수집** — 벤더별 지연 통계·재현·백테 정합용. 3벤더 공통 정책.
|
||||
3. **`KIWOOM_TICK_LIVE_MAX_LAG_SEC` 완전 폐기** → 새 스위치 `WS_TICK_DB_SAVE_LAG_CUT_ENABLED` 하나로 통일.
|
||||
4. **`param_search_optuna.py` 강제 세팅 제거** — DB env 로만 관리 (사용자 UI 존중).
|
||||
|
||||
### 9.3 새 스위치: `WS_TICK_DB_SAVE_LAG_CUT_ENABLED` (bool, 기본 **false** · 3벤더 공통)
|
||||
|
||||
| 값 | 동작 | 용도 |
|
||||
|----|------|------|
|
||||
| **false (기본)** | 키움/KIS/LS 모든 틱을 lag 무관 `ws_ticks` 전부 저장 | **B안** — 벤더 통계·재현·백테 정합 |
|
||||
| true | `lag > LIVE_FEED_FALLBACK_MAX_AGE_SEC` (기본 3초) 인 틱은 DB 미저장 | **A안** — 통계 확신 후 저장·매매 정합 통일 |
|
||||
|
||||
- 매매 RAM 은 **항상** `LIVE_FEED_FALLBACK_MAX_AGE_SEC` (기본 3초) 로 컷 (변경 없음).
|
||||
- 이 스위치는 **DB 저장 컷** 만 제어.
|
||||
|
||||
### 9.4 코드 진실 (수정 후)
|
||||
|
||||
| 경로 | 동작 |
|
||||
|------|------|
|
||||
| `kis_trader/engine/feed_fallback.py::bar_is_garbage` | **각 틱의 `recv_ts` 기준** wall-clock lag. `recv_ts` 없으면 봉끝 폴백 |
|
||||
| `kis_trader/engine/feed_fallback.py::candle_garbage_fallback_enabled` | 기본 **True** 복원 (실매 정합 논리 정정 후) |
|
||||
| `kis_trader/backtest/param_search_optuna.py` | `CANDLE_GARBAGE_FALLBACK`/`BACKTEST_USE_RUST` 강제 세팅 **제거** |
|
||||
| `kis_trader/ws/kiwoom_ws.py` | `_skip_persist` = `WS_TICK_DB_SAVE_LAG_CUT_ENABLED and lag > LIVE_FEED_FALLBACK` |
|
||||
| `kis_trader/ws/kis_ws.py` | `_skip_persist_kis` = 동일 로직 (기존엔 무조건 저장) |
|
||||
| `kis_trader/ws/ls_ws.py` | `_skip_persist_ls` = 동일 로직 (기존엔 skip_ram 시 저장 안 함) |
|
||||
|
||||
### 9.5 Parquet 초고속 틱 로더 (백테)
|
||||
|
||||
`kis_trader/backtest/breakout_tick_loader.py` · `kis_trader/utils/export_ticks_parquet.py`
|
||||
|
||||
- **Parquet = DB 스냅샷** (`export_ticks_parquet.py` 가 `ws_ticks` 를 필터 없이 그대로 export).
|
||||
- 스위치 OFF (기본) 상태 → DB 는 전부 저장 → Parquet 도 전부 포함 → **DB 로드와 완전 동일**.
|
||||
- 로드 후 `bar_is_garbage` (wall-clock recv_ts 기준) 가 동일하게 적용 → 실매 정합.
|
||||
- 즉 **Parquet 로더는 "로드 경로 초고속화"만** 이고, 쓰레기 스킵 판정은 후단(`candle_series`) 에서 동일.
|
||||
|
||||
### 9.6 시나리오 재검증 (수정 후)
|
||||
|
||||
| 시나리오 | 이전 (bar_end 기준) | 이후 (wall-clock 기준) | 실매 RAM |
|
||||
|----------|---------------------|------------------------|----------|
|
||||
| 유동성 낮은 종목: 09:00:05 유일 틱 | lag=55s → 쓰레기 스킵 ❌ | lag ≈ 0s (recv_ts 즉시) → 유효 ✅ | 유효 ✅ |
|
||||
| WS 재연결: 09:00:00 틱을 09:00:10 수신 | lag=59s → 스킵 | recv_ts-tick_time=10s → 스킵 ✅ | 스킵 (RAM 3초컷) ✅ |
|
||||
| 정상 실시간 틱 | lag < 60s → 유효 | lag ≈ 0s → 유효 ✅ | 유효 ✅ |
|
||||
|
||||
**결론: 백테 = 실매 정합 완료.**
|
||||
|
||||
### 9.7 향후 A안 (통계 확신 후)
|
||||
|
||||
1. `feed_collect_stats` 탭에서 벤더별 `lag > 3s` 비율 확인 (수 주간).
|
||||
2. 확신 서면 UI 에서 `WS_TICK_DB_SAVE_LAG_CUT_ENABLED = true` 로 전환.
|
||||
3. 이후 `ws_ticks` 는 lag > 3s 인 틱 제외되어 저장 (매매 RAM 컷과 완전 일치).
|
||||
4. `bar_is_garbage` 는 그대로 유지 (안전장치).
|
||||
|
||||
### 9.8 무엇을 하지 말 것
|
||||
|
||||
- ❌ 스위치를 **선제적으로 true** 로 바꾸지 말 것 (통계 부재 상태에서 벤더별 커버리지 손실 위험).
|
||||
- ❌ `bar_is_garbage` 를 다시 봉끝 기준으로 되돌리지 말 것 (실매 정합 깨짐).
|
||||
- ❌ `CANDLE_GARBAGE_FALLBACK=False` 로 다시 끄지 말 것 (12~22% 쓰레기는 봉끝 기준의 버그였음. wall-clock 기준으로는 훨씬 낮음).
|
||||
- ❌ `param_search_optuna.py` 에 `os.environ["..."]="..."` 강제 세팅 재도입 금지 (DB env 우회 → UI 무력화).
|
||||
|
||||
Reference in New Issue
Block a user