한투 호가 = 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 키 정리도 같이 들어갔습니다. 파일 단위로 나눌 수 없어서입니다.
132 lines
4.6 KiB
Python
132 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""어제(8/18) 키움 FID20 동결·지연 틱 버그가 오늘(8/19)에도 있는지 DB 검증."""
|
|
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
|
|
|
|
|
|
def main() -> None:
|
|
db = TradeDB()
|
|
cols = [r["Field"] for r in db.conn.execute("SHOW COLUMNS FROM ws_ticks").fetchall()]
|
|
print("ws_ticks cols:", cols)
|
|
need = {"code", "tick_time", "recv_ts", "source", "volume", "price"}
|
|
missing = need - set(cols)
|
|
if missing:
|
|
print("MISSING", missing)
|
|
return
|
|
has_raw = "tick_time_raw" in cols
|
|
raw_sel = "tick_time_raw" if has_raw else "NULL AS tick_time_raw"
|
|
print("tick_time_raw:", has_raw)
|
|
|
|
# recv_ts 초 단위, tick_time 은 HHMMSS 또는 YYYYMMDDHHMMSS
|
|
lag_expr = """
|
|
TIMESTAMPDIFF(
|
|
SECOND,
|
|
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'
|
|
),
|
|
recv_ts
|
|
)
|
|
"""
|
|
|
|
windows = [
|
|
("어제동결창 10:40-12:00", "2026-08-18 10:40:00", "2026-08-18 12:00:00"),
|
|
("어제아침 09:00-09:15", "2026-08-18 09:00:00", "2026-08-18 09:15:00"),
|
|
("오늘아침 09:00-now", "2026-08-19 09:00:00", "2026-08-19 23:59:59"),
|
|
("오늘전체 recv", "2026-08-19 00:00:00", "2026-08-19 23:59:59"),
|
|
]
|
|
|
|
for title, a, b in windows:
|
|
sql = (
|
|
"SELECT COUNT(*) AS n, "
|
|
"MIN(recv_ts) AS min_recv, MAX(recv_ts) AS max_recv, "
|
|
f"MIN({lag_expr}) AS min_lag, "
|
|
f"MAX({lag_expr}) AS max_lag, "
|
|
f"AVG({lag_expr}) AS avg_lag, "
|
|
f"SUM(CASE WHEN {lag_expr} > 5 THEN 1 ELSE 0 END) AS n_lag5, "
|
|
f"SUM(CASE WHEN {lag_expr} > 120 THEN 1 ELSE 0 END) AS n_lag120, "
|
|
f"SUM(CASE WHEN {lag_expr} > 600 THEN 1 ELSE 0 END) AS n_lag600 "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s AND recv_ts < %s"
|
|
)
|
|
row = db.conn.execute(sql, ("kiwoom", a, b)).fetchone()
|
|
print(f"\n== {title} kiwoom ==")
|
|
print(dict(row) if row else None)
|
|
|
|
sql2 = (
|
|
"SELECT COUNT(*) AS n, "
|
|
f"MAX({lag_expr}) AS max_lag, "
|
|
f"AVG({lag_expr}) AS avg_lag, "
|
|
f"SUM(CASE WHEN {lag_expr} > 5 THEN 1 ELSE 0 END) AS n_lag5 "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s AND recv_ts < %s"
|
|
)
|
|
rowk = db.conn.execute(sql2, ("kis", a, b)).fetchone()
|
|
print(" kis:", dict(rowk) if rowk else None)
|
|
|
|
# 오늘 키움: tick_time 최빈(동결 시 한 시각에 몰림)
|
|
print("\n== 오늘 키움 tick_time 상위 8 ==")
|
|
top = db.conn.execute(
|
|
"SELECT tick_time, COUNT(*) AS n, MIN(recv_ts) AS min_recv, MAX(recv_ts) AS max_recv "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s "
|
|
"GROUP BY tick_time ORDER BY n DESC LIMIT 8",
|
|
("kiwoom", "2026-08-19 09:00:00"),
|
|
).fetchall()
|
|
for r in top or []:
|
|
print(dict(r))
|
|
|
|
print("\n== 어제동결창 키움 tick_time 상위 5 ==")
|
|
top_y = db.conn.execute(
|
|
"SELECT tick_time, COUNT(*) AS n, MIN(recv_ts) AS min_recv, MAX(recv_ts) AS max_recv "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s AND recv_ts < %s "
|
|
"GROUP BY tick_time ORDER BY n DESC LIMIT 5",
|
|
("kiwoom", "2026-08-18 10:40:00", "2026-08-18 12:00:00"),
|
|
).fetchall()
|
|
for r in top_y or []:
|
|
print(dict(r))
|
|
|
|
print("\n== 오늘 키움 lag>120 샘플 8 ==")
|
|
samples = db.conn.execute(
|
|
"SELECT code, tick_time, "
|
|
+ raw_sel
|
|
+ ", recv_ts, price, volume, "
|
|
+ lag_expr
|
|
+ " AS lag_sec "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s "
|
|
f"AND {lag_expr} > 120 "
|
|
"ORDER BY recv_ts DESC LIMIT 8",
|
|
("kiwoom", "2026-08-19 09:00:00"),
|
|
).fetchall()
|
|
if not samples:
|
|
print("(없음)")
|
|
for r in samples or []:
|
|
print(dict(r))
|
|
|
|
print("\n== 오늘 키움 최근 5건 ==")
|
|
recent = db.conn.execute(
|
|
"SELECT code, tick_time, "
|
|
+ raw_sel
|
|
+ ", recv_ts, price, "
|
|
+ lag_expr
|
|
+ " AS lag_sec "
|
|
"FROM ws_ticks WHERE source=%s AND recv_ts >= %s "
|
|
"ORDER BY recv_ts DESC LIMIT 5",
|
|
("kiwoom", "2026-08-19 09:00:00"),
|
|
).fetchall()
|
|
for r in recent or []:
|
|
print(dict(r))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|