diff --git a/agents/stock/workspace/scripts/behive_web.py b/agents/stock/workspace/scripts/behive_web.py index a5d72d0..38b959f 100644 --- a/agents/stock/workspace/scripts/behive_web.py +++ b/agents/stock/workspace/scripts/behive_web.py @@ -1845,7 +1845,7 @@ def _render_row(c: dict, source: str = 'watchlist') -> str: if c.get('_show_day_change') and isinstance(day_pct, (int, float)): d_sign = '+' if day_pct >= 0 else '' d_cls = 'day-pct up' if day_pct > 0 else ('day-pct down' if day_pct < 0 else 'day-pct neutral') - price_cell = f'{mark_html}{price:,}{d_sign}{day_pct:.2f}%' + price_cell = f'{mark_html}{price:,}{d_sign}{day_pct:.2f}%' else: price_cell = f'{mark_html}{price:,}' if mode == 'watching' and isinstance(ref_price, (int, float)) and ref_price > 0: @@ -8944,8 +8944,11 @@ window.openPinModal = openPinModal; function paintPrice(code,q){ // 현재가 숫자 span(.rt-px)만 갱신 — 등락률·매매표시·당일정산(실현손익) 셀은 .rt-px가 없어 자동 제외. var t=q.price.toLocaleString(); + var pctstr=(q.pct>=0?'+':'')+q.pct.toFixed(2)+'%'; document.querySelectorAll(selByCode(code)).forEach(function(r){ r.querySelectorAll('.rt-px').forEach(function(el){ if(el.textContent!==t) el.textContent=t; }); // 값 바뀔 때만 → 깜빡임 방지 + // 관심·감시 행 등락률 — KRX 기준이라 0B 피드 pct를 그대로 사용(보유행은 NXT 기준이라 paintDay가 따로 처리). + var dp=r.querySelector('.rt-wl-daypct'); if(dp){ dp.textContent=pctstr; setUD(dp,q.pct); } }); } function paintVI(code,active){ diff --git a/agents/stock/workspace/scripts/realtime_hub.py b/agents/stock/workspace/scripts/realtime_hub.py index 1471e8d..7c3db2a 100644 --- a/agents/stock/workspace/scripts/realtime_hub.py +++ b/agents/stock/workspace/scripts/realtime_hub.py @@ -220,6 +220,7 @@ class RealtimeHub: # 3) 수신 루프 _last_mkt_check = time.time() + empty_streak = 0 # 연속 빈응답 카운터 — tight busy-loop 방지 while not self._stop.is_set(): if time.time() - self.connected_at > _MAX_CONN_AGE: sys.stderr.write('[realtime-hub] 토큰 만료 전 선제 재연결\n') @@ -233,9 +234,16 @@ class RealtimeHub: try: raw = ws.recv() except websocket.WebSocketTimeoutException: + empty_streak = 0 # 타임아웃은 정상 idle continue if not raw: + # 빈응답이 예외 없이 연속되면(드묾) tight loop → 5회 누적 시 재연결로 탈출 + empty_streak += 1 + if empty_streak >= 5: + sys.stderr.write('[realtime-hub] 빈 응답 연속 → 연결 재설정\n') + return continue + empty_streak = 0 self._handle(raw) finally: self._connected.clear()