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:
@@ -452,14 +452,19 @@ def orderbook_row_lag_seconds(row: Dict[str, Any]) -> Optional[int]:
|
||||
|
||||
|
||||
def candle_garbage_fallback_enabled() -> bool:
|
||||
"""쓰레기 봉 스킵(2차·3차 소스 폴백) 활성 여부.
|
||||
|
||||
2026-09-06: 기본 True 복원. bar_is_garbage 를 wall-clock(recv_ts) 기준으로 정정 후
|
||||
실매 RAM 3초컷과 동일 논리로 동작 → 실매↔백테 정합. (docs/정합성.md §9)
|
||||
BACKTEST_CANDLE_GARBAGE_OFF=1 은 실험용 강제 OFF (일반 운영은 사용 금지).
|
||||
"""
|
||||
import os
|
||||
if os.environ.get("BACKTEST_CANDLE_GARBAGE_OFF") == "1":
|
||||
return False
|
||||
# 사용자 지시(쓰레기스킵 전면 폐지)에 따라 기본값을 False로 변경
|
||||
try:
|
||||
return bool(get_env_bool("CANDLE_GARBAGE_FALLBACK", False))
|
||||
return bool(get_env_bool("CANDLE_GARBAGE_FALLBACK", True))
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def bar_end_datetime(candle_time: str, tf_min: int):
|
||||
@@ -499,7 +504,14 @@ def bar_is_garbage(
|
||||
source: str,
|
||||
missing_policy: str = "hole",
|
||||
) -> bool:
|
||||
"""그 분·그 source 틱이 없거나 전부 봉끝 대비 읽기나이 초과면 True(구멍).
|
||||
"""그 분·그 source 틱이 없거나 전부 수신시각(recv_ts) 대비 읽기나이 초과면 True(구멍).
|
||||
|
||||
2026-09-06 정정 (docs/정합성.md): 기준 시각을 **봉끝** → **각 틱의 recv_ts** 로 변경.
|
||||
이유: 실매 RAM 3초컷은 `wall-clock(수신순간) - tick_time` 기준.
|
||||
이전 봉끝 기준은 유동성 낮은 종목·봉 시작 근처 틱이 부당 스킵되어 실매와 갭 발생.
|
||||
이제 실매·백테 동일 wall-clock lag 판정 → 정합.
|
||||
|
||||
fallback: recv_ts 없으면 옛 봉끝 기준(레거시 데이터 호환).
|
||||
|
||||
missing_policy:
|
||||
hole — 0건이면 쓰레기 (옵투나 공책)
|
||||
@@ -522,11 +534,30 @@ def bar_is_garbage(
|
||||
if not bucket:
|
||||
return str(missing_policy or "hole").strip().lower() == "hole"
|
||||
bar_end = bar_end_datetime(candle_time, tf_min)
|
||||
if bar_end is None:
|
||||
return False
|
||||
for t in bucket:
|
||||
raw = str(t.get("tick_time_raw") or t.get("tick_time") or "")
|
||||
lag = packet_lag_seconds(raw, now_dt=bar_end)
|
||||
# 각 틱의 wall-clock 기준(recv_ts) 대비 lag = 실매 RAM 판정과 동일 논리
|
||||
recv_dt: Optional[datetime] = None
|
||||
rts = t.get("recv_ts")
|
||||
if rts is not None:
|
||||
try:
|
||||
if isinstance(rts, datetime):
|
||||
recv_dt = rts
|
||||
else:
|
||||
s = str(rts).strip().replace("T", " ")
|
||||
# 형식 예: '2026-09-04 09:00:05.123' 또는 '2026-09-04 09:00:05'
|
||||
if "." in s:
|
||||
recv_dt = datetime.strptime(s[:19], "%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
recv_dt = datetime.strptime(s[:19], "%Y-%m-%d %H:%M:%S")
|
||||
except Exception:
|
||||
recv_dt = None
|
||||
# recv_ts 없거나 파싱 실패 → 봉끝 기준(레거시 폴백, 실매보다 보수적)
|
||||
base_dt = recv_dt if recv_dt is not None else bar_end
|
||||
if base_dt is None:
|
||||
# 봉끝도 파싱 실패면 판정 포기 → False(버리지 않음)
|
||||
return False
|
||||
lag = packet_lag_seconds(raw, now_dt=base_dt)
|
||||
if not is_feed_read_stale(lag):
|
||||
return False
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user