feat: 자산웹 실시간(WS) NXT 통합·시세 중복 제거·연결상태 점
- realtime_hub: 0B/1h 구독을 _AL(통합/SOR)로 전환 → 정규장 KRX + NXT 시간대 NXT 체결을 한 구독으로 수신(수신 시 거래소 접미사 제거 후 bare 저장). NXT 당일평가손익 실시간 갱신 미동작 근본 수정. 0B 시/고/저(16/17/18) 캡처 - behive_web: WS 연결 시 보유 가격보정 ka10095 2콜 skip(완전 중복), 관심·감시 시세도 허브 메모리 우선 충당(OHLC·마크는 서버 렌더 유지, mark는 exchange 필드로 판정) → 시세성 키움 호출 중복 제거 - VI 배지: 자동갱신 swap 직후 마지막 WS 상태 즉시 재적용(깜빡임 제거), codesOnPage가 보유행(:held)·영문 6자리 코드도 수집 → 보유종목까지 VI 표시 - 상단바 WS 연결 상태 점(초록/빨강/회색) 추가 + 탭 시 상태 토스트 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,7 @@ class RealtimeHub:
|
||||
def __init__(self, account_label: str | None = None):
|
||||
self._label = account_label or kc._default_account_label()
|
||||
self._lock = threading.Lock()
|
||||
self._quotes: dict[str, dict] = {} # code -> {price, change, pct, ts, exchange}
|
||||
self._quotes: dict[str, dict] = {} # code -> {price, change, pct, open, high, low, ts, exchange}
|
||||
self._vi: dict[str, dict] = {} # code -> {active, kind, trigger_price, ...}
|
||||
self._subs: set[str] = set() # 현재 구독 종목코드(정규화)
|
||||
self._ws: websocket.WebSocket | None = None
|
||||
@@ -261,17 +261,23 @@ class RealtimeHub:
|
||||
return
|
||||
self._ws.send(json.dumps(payload))
|
||||
|
||||
@staticmethod
|
||||
def _al(codes: list[str]) -> list[str]:
|
||||
# 통합(SOR) 거래소코드 `_AL` 접미사 — 정규장 KRX 체결 + NXT 시간대 NXT 체결을
|
||||
# 한 구독으로 수신. bare 코드는 KRX 단독이라 NXT(15:30~20:00)에 시세가 끊긴다. (PDF 0B/1h 스펙)
|
||||
return [f'{c}_AL' for c in codes]
|
||||
|
||||
def _send_reg(self, codes: list[str]) -> None:
|
||||
# 0B(체결) + 1h(VI) 동시 등록. refresh=1 → 기존 구독 유지하며 추가.
|
||||
self._send({
|
||||
'trnm': 'REG', 'grp_no': '1', 'refresh': '1',
|
||||
'data': [{'item': codes, 'type': ['0B', '1h']}],
|
||||
'data': [{'item': self._al(codes), 'type': ['0B', '1h']}],
|
||||
})
|
||||
|
||||
def _send_remove(self, codes: list[str]) -> None:
|
||||
self._send({
|
||||
'trnm': 'REMOVE', 'grp_no': '1',
|
||||
'data': [{'item': codes, 'type': ['0B', '1h']}],
|
||||
'data': [{'item': self._al(codes), 'type': ['0B', '1h']}],
|
||||
})
|
||||
|
||||
# ---- 수신 처리 ----
|
||||
@@ -302,7 +308,9 @@ class RealtimeHub:
|
||||
|
||||
def _handle_real(self, blk: dict) -> None:
|
||||
typ = blk.get('type')
|
||||
code = kc._clean_code(blk.get('item') or '')
|
||||
# 통합(_AL)·NXT(_NX) 구독은 item이 `039490_AL` 형태로 echo됨 → 거래소 접미사를 떼고
|
||||
# bare 코드로 저장해야 get_quotes(bare) 매칭이 유지된다. 키움 종목코드엔 '_'가 없다.
|
||||
code = kc._clean_code((blk.get('item') or '').split('_')[0])
|
||||
vals = blk.get('values') or {}
|
||||
if not code:
|
||||
return
|
||||
@@ -312,6 +320,9 @@ class RealtimeHub:
|
||||
'price': _abs_int(vals.get('10')),
|
||||
'change': kc._to_int(vals.get('11')),
|
||||
'pct': kc._to_float(vals.get('12')),
|
||||
'open': _abs_int(vals.get('16')), # 시가
|
||||
'high': _abs_int(vals.get('17')), # 고가
|
||||
'low': _abs_int(vals.get('18')), # 저가
|
||||
'exchange': (vals.get('9081') or '').strip(),
|
||||
'ts': datetime.now(KST).isoformat(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user