이번에 들어간 내용
한투 호가 = 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 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
This commit is contained in:
142
scratch/compare_kiwoom_kis_tick_gaps.py
Normal file
142
scratch/compare_kiwoom_kis_tick_gaps.py
Normal file
@@ -0,0 +1,142 @@
|
||||
#!/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))
|
||||
Reference in New Issue
Block a user