Files
kis_bot/scratch/compare_kiwoom_kis_tick_gaps.py
Your Name 0ecac7cb95 이번에 들어간 내용
한투 호가 = 2번째 앱키 전용
키 없거나 start 실패 시 메인에 H0STASP0 안 붙임. 운영설정 WS_ORDERBOOK_SAVE_KIS 빨간 danger.

LS RAM 합집합
후보∪보유∪영구∪grace. sync_targets와 split reconcile 둘 다. 틱 DB 영구 게이트는 그대로.

분봉 쓰레기 → 다음 소스 봉 통째
그 분 틱 0건이거나 전부 봉끝 대비 LIVE_FEED_FALLBACK_MAX_AGE_SEC 초과면 구멍. 메인 WS → 2차 → LS → REST → rollup. CANDLE_GARBAGE_FALLBACK 기본 true.

파일: feed_fallback.py(신규), ws_manager.py, kis_ws.py, candle_series.py, bt_candle_source.py, live_config_schema.py, database.py, 스모크, MD 2개.

같은 ws_manager/database/kis_ws/live_config에는 직전 커밋 이후 쌓여 있던 시세 폴백·ENV 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
2026-08-19 22:11:31 +09:00

143 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""오늘 키움 vs 한투 종목별 틱 공백 비교 (1회)."""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from database import TradeDB
TT = """
STR_TO_DATE(
CASE
WHEN CHAR_LENGTH(tick_time) >= 14 THEN LEFT(tick_time, 14)
WHEN CHAR_LENGTH(tick_time) = 6 THEN CONCAT(DATE_FORMAT(recv_ts, '%%Y%%m%%d'), tick_time)
ELSE NULL
END,
'%%Y%%m%%d%%H%%i%%s'
)
"""
db = TradeDB()
cols = [r["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ws_ticks").fetchall()]
need = {"code", "tick_time", "recv_ts", "source", "id"}
missing = need - set(cols)
if missing:
raise SystemExit(f"ws_ticks missing {missing} have={cols}")
t0 = "2026-08-19 09:00:00"
t1 = "2026-08-19 09:35:00"
print("window", t0, "~", t1)
print("\n== source 전체 (recv 연속 공백 / tick_time 연속 공백) ==")
rows = db.conn.execute(
f"""
SELECT source,
COUNT(*) n,
COUNT(DISTINCT code) codes,
MAX(recv_gap) max_recv_gap,
AVG(recv_gap) avg_recv_gap,
SUM(recv_gap >= 10) n_recv_ge10,
SUM(recv_gap >= 20) n_recv_ge20,
MAX(tt_gap) max_tt_gap,
AVG(tt_gap) avg_tt_gap,
SUM(tt_gap >= 10) n_tt_ge10,
SUM(tt_gap >= 20) n_tt_ge20
FROM (
SELECT source, code,
TIMESTAMPDIFF(SECOND, LAG(recv_ts) OVER (PARTITION BY source, code ORDER BY id), recv_ts) recv_gap,
TIMESTAMPDIFF(SECOND, LAG({TT}) OVER (PARTITION BY source, code ORDER BY {TT}, id), {TT}) tt_gap
FROM ws_ticks
WHERE recv_ts >= %s AND recv_ts < %s AND source IN ('kiwoom','kis')
) x
GROUP BY source
""",
(t0, t1),
).fetchall()
for r in rows:
print(dict(r))
print("\n== 키움 recv 공백 TOP15 (같은 종목 한투 대비) ==")
rows = db.conn.execute(
f"""
SELECT k.code,
k.n_kw n_kiwoom, s.n_kis,
k.max_recv max_recv_kw, s.max_recv max_recv_kis,
(k.max_recv - IFNULL(s.max_recv, 0)) delta_recv,
k.max_tt max_tt_kw, s.max_tt max_tt_kis
FROM (
SELECT code, COUNT(*) n_kw,
MAX(recv_gap) max_recv, MAX(tt_gap) max_tt
FROM (
SELECT code,
TIMESTAMPDIFF(SECOND, LAG(recv_ts) OVER (PARTITION BY code ORDER BY id), recv_ts) recv_gap,
TIMESTAMPDIFF(SECOND, LAG({TT}) OVER (PARTITION BY code ORDER BY {TT}, id), {TT}) tt_gap
FROM ws_ticks
WHERE source='kiwoom' AND recv_ts >= %s AND recv_ts < %s
) a
GROUP BY code
) k
LEFT JOIN (
SELECT code, COUNT(*) n_kis,
MAX(recv_gap) max_recv, MAX(tt_gap) max_tt
FROM (
SELECT code,
TIMESTAMPDIFF(SECOND, LAG(recv_ts) OVER (PARTITION BY code ORDER BY id), recv_ts) recv_gap,
TIMESTAMPDIFF(SECOND, LAG({TT}) OVER (PARTITION BY code ORDER BY {TT}, id), {TT}) tt_gap
FROM ws_ticks
WHERE source='kis' AND recv_ts >= %s AND recv_ts < %s
) b
GROUP BY code
) s ON s.code = k.code
ORDER BY k.max_recv DESC
LIMIT 15
""",
(t0, t1, t0, t1),
).fetchall()
for r in rows:
print(dict(r))
print("\n== 025980 단독 ==")
rows = db.conn.execute(
f"""
SELECT source, COUNT(*) n,
MAX(recv_gap) max_recv, AVG(recv_gap) avg_recv,
MAX(tt_gap) max_tt, AVG(tt_gap) avg_tt,
SUM(recv_gap >= 10) n_recv_ge10
FROM (
SELECT source,
TIMESTAMPDIFF(SECOND, LAG(recv_ts) OVER (PARTITION BY source ORDER BY id), recv_ts) recv_gap,
TIMESTAMPDIFF(SECOND, LAG({TT}) OVER (PARTITION BY source ORDER BY {TT}, id), {TT}) tt_gap
FROM ws_ticks
WHERE code=%s AND recv_ts >= %s AND recv_ts < %s AND source IN ('kiwoom','kis')
) x
GROUP BY source
""",
("025980", t0, t1),
).fetchall()
for r in rows:
print(dict(r))
print("\n== 키움 recv>=10초 공백이 있는 종목 수 vs 한투 ==")
for src in ("kiwoom", "kis"):
r = db.conn.execute(
f"""
SELECT COUNT(*) codes_with_gap10 FROM (
SELECT code, MAX(recv_gap) mx FROM (
SELECT code,
TIMESTAMPDIFF(SECOND, LAG(recv_ts) OVER (PARTITION BY code ORDER BY id), recv_ts) recv_gap
FROM ws_ticks
WHERE source=%s AND recv_ts >= %s AND recv_ts < %s
) a
GROUP BY code
HAVING mx >= 10
) z
""",
(src, t0, t1),
).fetchone()
print(src, dict(r))