Files
kis_trader/scratch/verify_garbage_skip.py
Your Name e1ac8d119b chore: 작업 중 발생한 부수적 변경 사항 및 누락된 파일 전체 커밋
- 프론트엔드 UI 업데이트 (backtest.html, backtest.js) 엔진 라디오 버튼 통합 관련 반영
- Rust 플러그인(kis_rust_core) 및 컴파일 소스코드 추가
- CLI 백테스트 스크립트 수정 및 최신화
- 기타 스크래치 테스트 스크립트, 로그 요약 마크다운(.md) 등 누락 파일 일괄 반영
- 추가적으로 아직 발견되지 않은 엣지 케이스나 렌더링 오류가 포함되어 있을 가능성이 있음
2026-09-06 17:04:50 +09:00

120 lines
4.3 KiB
Python

import sys
import os
import time
import datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from database import TradeDB
from kis_trader.engine.feed_fallback import packet_lag_seconds, is_feed_read_stale, bar_end_datetime, tick_in_bar_bucket
def old_bar_is_garbage(ticks, candle_time, tf_min=1):
bucket = []
for t in ticks:
raw = str(t.get("tick_time_raw") or t.get("tick_time") or "")
if tick_in_bar_bucket(str(raw), candle_time, tf_min):
bucket.append(t)
if not bucket:
return True, "No ticks"
bar_end = bar_end_datetime(candle_time, tf_min)
if bar_end is None:
return False, "No bar_end"
stale_lags = []
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)
stale = is_feed_read_stale(lag)
if not stale:
return False, f"Valid tick lag: {lag:.2f}s"
stale_lags.append(lag)
return True, f"All ticks stale relative to bar_end (min lag={min(stale_lags):.2f}s)"
def main():
db = TradeDB()
# 동국알앤에스 (323350) 2026-09-04
code = "323350"
day = "20260904"
print(f"[{code}] {day} 캔들 조회 중...")
candles = db.conn.execute(
"SELECT * FROM ws_candles WHERE code=%s AND candle_time LIKE %s AND timeframe=1 ORDER BY candle_time ASC",
(code, f"{day}%%")
).fetchall()
candles = [dict(c) for c in candles]
print(f"{len(candles)}개 1분봉 캔들 확보.\n")
print(f"[{code}] {day} 틱 조회 중...")
ticks = db.conn.execute(
"SELECT * FROM ws_ticks WHERE code=%s AND tick_time LIKE %s ORDER BY tick_time ASC",
(code, f"{day}%%")
).fetchall()
ticks_dict = []
for t in ticks:
ticks_dict.append({
"tick_time": t["tick_time"],
"tick_time_raw": t["tick_time"],
"price": t["price"],
"volume": t["volume"]
})
print(f"{len(ticks_dict)}개 틱 확보.\n")
if not ticks_dict:
print("틱 데이터가 없어 검증을 종료합니다.")
return
garbage_count = 0
valid_count = 0
empty_count = 0
print("--- 🗑️ 기존 로직에 의해 '쓰레기(Garbage)'로 판정된 정상 캔들 샘플 ---\n")
for c in candles:
# ws_candles는 candle_time, ls_ws_candles는 datetime 컬럼을 가짐
ct = c.get("candle_time") or c.get("datetime")
if not ct: continue
bucket_ticks = [t for t in ticks_dict if tick_in_bar_bucket(t["tick_time"], ct, 1)]
is_garbage, reason = old_bar_is_garbage(bucket_ticks, ct, 1)
if len(bucket_ticks) == 0:
empty_count += 1
continue
if is_garbage:
garbage_count += 1
if garbage_count <= 5: # 5개만 샘플 출력
vol = sum(t["volume"] for t in bucket_ticks)
last_tick_time = bucket_ticks[-1]["tick_time"]
bar_end_time = bar_end_datetime(ct, 1)
print(f"[Garbage 오판] 캔들시간: {ct}")
print(f" -> 분봉 내 체결 틱 수: {len(bucket_ticks)}")
print(f" -> 분봉 내 거래량: {vol}")
print(f" -> 마지막 체결 틱 시간: {last_tick_time}")
print(f" -> 분봉 종료 정각 시간: {bar_end_time.strftime('%Y%m%d%H%M%S')}")
print(f" -> 판정 이유: {reason}")
print(f" => 진짜 쓰레기? ❌ 정상 캔들인데 마지막 체결이 정각보다 몇초 일찍 끝났을 뿐입니다.\n")
else:
valid_count += 1
print(f"=== 📊 최종 결과 요약 ===")
print(f"총 캔들 수: {len(candles)}")
print(f"거래가 없었던 빈 캔들: {empty_count}")
print(f"정상 판정(통과) 캔들: {valid_count}")
print(f"가비지로 오판(삭제)된 캔들: {garbage_count}")
if (valid_count + garbage_count) > 0:
drop_rate = (garbage_count / (valid_count + garbage_count)) * 100
print(f"🚀 거래가 있었던 캔들 중 멀쩡한데 버려진 비율: {drop_rate:.1f}%")
if __name__ == "__main__":
main()