feat: realtime_hub 장외 대기 — 다음 영업일까지(6h 상한)
30분 고정 폴링 → 다음 영업일 07:00까지 계산해 자되 6시간 상한. 주말·휴장·연휴 정확히 건너뜀(_next_preopen_dt + _load_holidays). 휴장일 깨우기 48→4회/일. 6h 상한은 날짜계산 오류 시에도 자가교정하는 backstop. 개장 임박(07:00~08:00)은 여전히 60s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -45,9 +45,9 @@ _BACKOFF_MAX = 30.0
|
|||||||
_MAX_CONN_AGE = 25 * 60
|
_MAX_CONN_AGE = 25 * 60
|
||||||
# recv 타임아웃 — 이 시간 내 아무것도 안 오면 루프 돌며 stop/age 체크
|
# recv 타임아웃 — 이 시간 내 아무것도 안 오면 루프 돌며 stop/age 체크
|
||||||
_RECV_TIMEOUT = 5.0
|
_RECV_TIMEOUT = 5.0
|
||||||
# 장 마감 시간대 대기 주기 — 개장 임박(평일 07:00~08:00, NXT 프리마켓 08:00 직전)엔 촘촘히, 그 외(밤·주말·휴장)엔 드물게.
|
# 장 마감 시간대 대기 — 개장 임박(평일 07:00~08:00)엔 1분, 그 외엔 다음 영업일 07:00까지 자되 6시간 상한.
|
||||||
_PREOPEN_POLL_SEC = 60.0 # 개장 임박: 1분마다 → 08:00 개장을 1분 안에 포착
|
_PREOPEN_POLL_SEC = 60.0 # 개장 임박: 1분마다 → 08:00 개장을 1분 안에 포착
|
||||||
_DEEP_CLOSED_POLL_SEC = 1800.0 # 밤·주말·휴장: 30분마다 (자가교정 상한, 헛바퀴 최소화)
|
_CLOSED_WAIT_CAP = 6 * 3600.0 # 밤·주말·휴장: 다음 개장까지 자되 이 상한(자가교정용 — 날짜 계산 오류 시에도 6h마다 재확인)
|
||||||
|
|
||||||
|
|
||||||
def _ws_url() -> str:
|
def _ws_url() -> str:
|
||||||
@@ -147,18 +147,32 @@ class RealtimeHub:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _next_preopen_dt(now: datetime, holidays: set) -> datetime:
|
||||||
|
"""now 이후 가장 가까운 영업일(평일·비휴장) 07:00 datetime. 주말·휴장·연휴 건너뜀."""
|
||||||
|
for i in range(0, 15): # 최대 2주(연휴) 탐색
|
||||||
|
d = now + timedelta(days=i)
|
||||||
|
if d.weekday() < 5 and d.strftime('%Y-%m-%d') not in holidays:
|
||||||
|
cand = d.replace(hour=7, minute=0, second=0, microsecond=0)
|
||||||
|
if cand > now:
|
||||||
|
return cand
|
||||||
|
return now + timedelta(hours=6) # 안전 fallback
|
||||||
|
|
||||||
def _closed_wait_seconds(self) -> float:
|
def _closed_wait_seconds(self) -> float:
|
||||||
"""장 외 대기 시간. 평일 장외(closed)이고 NXT 프리마켓(08:00) 임박(07:00~08:00)이면 60s,
|
"""장 외 대기 시간.
|
||||||
그 외(밤·주말·휴장)는 30분. 휴장·주말은 phase가 'closed' 아니라 자동으로 30분."""
|
- 평일 장외(closed)이고 NXT 프리마켓 임박(07:00~08:00) → 60s (촘촘히)
|
||||||
|
- 그 외(밤·주말·휴장) → 다음 영업일 07:00까지 자되 6시간 상한 (헛바퀴 제거 + 자가교정).
|
||||||
|
판정 실패 시 보수적으로 60s."""
|
||||||
try:
|
try:
|
||||||
import behive_web
|
import behive_web
|
||||||
phase = behive_web._market_phase_state().get('phase')
|
now = datetime.now(KST)
|
||||||
hm = datetime.now(KST).hour * 60 + datetime.now(KST).minute
|
hm = now.hour * 60 + now.minute
|
||||||
if phase == 'closed' and 7 * 60 <= hm < 8 * 60:
|
if behive_web._market_phase_state().get('phase') == 'closed' and 7 * 60 <= hm < 8 * 60:
|
||||||
return _PREOPEN_POLL_SEC
|
return _PREOPEN_POLL_SEC
|
||||||
|
target = self._next_preopen_dt(now, behive_web._load_holidays())
|
||||||
|
return max(60.0, min((target - now).total_seconds(), _CLOSED_WAIT_CAP))
|
||||||
except Exception:
|
except Exception:
|
||||||
return _PREOPEN_POLL_SEC # 실패 시 보수적으로 자주 확인
|
return _PREOPEN_POLL_SEC
|
||||||
return _DEEP_CLOSED_POLL_SEC
|
|
||||||
|
|
||||||
def _run(self) -> None:
|
def _run(self) -> None:
|
||||||
backoff = _BACKOFF_START
|
backoff = _BACKOFF_START
|
||||||
|
|||||||
Reference in New Issue
Block a user