fix(시세): LS spill-only·토큰 파일캐시·호가 snap_time 3초컷
- _feed_fallback 미러 OFF, LS cap/grace/hold RAM을 KIS·키움 spill과 정합 - LS 접근토큰 .ls_token_cache_*.json (재시작 재사용, revoke 루프 없음) - 호가 RAM을 틱과 동일 LIVE_FEED_FALLBACK(snap_time)로 컷, 필터 max_age=0은 유지 - 익절 지정가 로그에 실제 호가 벤더(kis/kiwoom/ls 1·2·3차) 표기 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,7 @@ KIS 공식 스펙:
|
||||
'해외주식 실시간시세' 신청이 안 돼 있으면 HDFSCNT0 는 **15분 지연**으로 옵니다.
|
||||
15분봉 전략에선 신청 필수. 미신청 시에도 동작은 하나 데이터가 지연됩니다.
|
||||
|
||||
KIS 안티-어뷰즈 정책 준수: 재연결 횟수/시간 제한 + 지수 백오프 (국내 수신기와 동일 사상).
|
||||
KIS 안티-어뷰즈 정책 준수: 재연결 횟수/시간 제한 + 단계 대기 (WS_RECONNECT_DELAY_SECS, 기본 1,3,5,7,10초).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -37,6 +37,8 @@ import requests
|
||||
|
||||
logger = logging.getLogger("KISWebSocketOverseas")
|
||||
|
||||
from .ws_reconnect_backoff import ws_reconnect_delay_for_attempt
|
||||
|
||||
try:
|
||||
from kis_trader.utils.env import get_env_from_db, get_env_bool, get_env_int, get_env_float
|
||||
except ImportError: # 단독 실행 fallback
|
||||
@@ -83,8 +85,7 @@ class KISOverseasWebSocket:
|
||||
MAX_SUBSCRIPTIONS = 41
|
||||
MAX_RECONNECT_ATTEMPTS = 10
|
||||
MAX_RECONNECTS_PER_HOUR = 6
|
||||
RECONNECT_BASE_DELAY_SEC = 5.0
|
||||
RECONNECT_MAX_DELAY_SEC = 300.0
|
||||
# 재연결 sleep — ws_reconnect_backoff (기본 1,3,5,7,10초)
|
||||
STABLE_CONN_RESET_SEC = 300.0
|
||||
APPROVAL_KEY_CACHE_SEC = 82800
|
||||
|
||||
@@ -124,7 +125,6 @@ class KISOverseasWebSocket:
|
||||
|
||||
self._reconnect_count = 0
|
||||
self._reconnect_times: list = []
|
||||
self._reconnect_delay = self.RECONNECT_BASE_DELAY_SEC
|
||||
self._last_connect_time = 0.0
|
||||
|
||||
self._candle_agg: Optional[Any] = None
|
||||
@@ -511,7 +511,6 @@ class KISOverseasWebSocket:
|
||||
)
|
||||
self._reconnect_count = 0
|
||||
self._reconnect_times = []
|
||||
self._reconnect_delay = self.RECONNECT_BASE_DELAY_SEC
|
||||
_instant_streak = 0
|
||||
for _ in range(int(wait_sec // 60)):
|
||||
if not self._running:
|
||||
@@ -542,7 +541,6 @@ class KISOverseasWebSocket:
|
||||
and self._reconnect_count > 0):
|
||||
self._reconnect_count = 0
|
||||
self._reconnect_times = []
|
||||
self._reconnect_delay = self.RECONNECT_BASE_DELAY_SEC
|
||||
|
||||
self._reconnect_times = [t for t in self._reconnect_times if now - t < 3600]
|
||||
if len(self._reconnect_times) >= self.MAX_RECONNECTS_PER_HOUR:
|
||||
@@ -557,8 +555,8 @@ class KISOverseasWebSocket:
|
||||
|
||||
# 캐시 approval 재사용만 (force_refresh 금지 — 국내 세션 무효화 방지)
|
||||
if not self._get_approval_key(force_refresh=False):
|
||||
time.sleep(self._reconnect_delay)
|
||||
self._reconnect_delay = min(self._reconnect_delay * 2, self.RECONNECT_MAX_DELAY_SEC)
|
||||
_attempt = max(1, self._reconnect_count)
|
||||
time.sleep(ws_reconnect_delay_for_attempt(_attempt))
|
||||
continue
|
||||
|
||||
# 국내가 아직 붙잡고 있으면 짧게 양보 대기 (재발급 없음)
|
||||
@@ -579,8 +577,9 @@ class KISOverseasWebSocket:
|
||||
0.0,
|
||||
float(get_env_float("KIS_WS_RECONNECT_SESSION_WAIT_SEC", 2.0) or 2.0),
|
||||
)
|
||||
time.sleep(max(self._reconnect_delay, sess_wait))
|
||||
self._reconnect_delay = min(self._reconnect_delay * 2, self.RECONNECT_MAX_DELAY_SEC)
|
||||
_attempt = self._reconnect_count - 1
|
||||
_delay = ws_reconnect_delay_for_attempt(_attempt)
|
||||
time.sleep(max(_delay, sess_wait))
|
||||
|
||||
_conn_start = time.time()
|
||||
try:
|
||||
@@ -607,7 +606,6 @@ class KISOverseasWebSocket:
|
||||
|
||||
def _on_open(self, ws) -> None:
|
||||
self._connected = True
|
||||
self._reconnect_delay = self.RECONNECT_BASE_DELAY_SEC
|
||||
self._last_connect_time = time.time()
|
||||
logger.info("✅ KIS 해외 WebSocket 연결 성공 (HDFSCNT0)")
|
||||
with self._sym_lock:
|
||||
|
||||
Reference in New Issue
Block a user