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()