From 51d0384ed43bbc22bd2fbda80e4596d17539bc79 Mon Sep 17 00:00:00 2001 From: hyowons Date: Wed, 10 Jun 2026 14:08:14 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=B6=84=EB=B4=89=20=ED=81=AC=EB=A1=9C?= =?UTF-8?q?=EC=8A=A4=ED=97=A4=EC=96=B4=20=ED=88=B4=ED=8C=81=EC=97=90=20'?= =?UTF-8?q?=EB=8B=B9=EC=9D=BC=EC=8B=9C=EA=B0=80=20=EB=8C=80=EB=B9=84=20?= =?UTF-8?q?=EB=93=B1=EB=9D=BD=EB=A5=A0'=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 분봉 차트 클릭(세로선) 시 OHLC 팝업에 그 봉 종가의 당일 시가(첫 봉) 대비 등락률을 표시 — 그 시점까지의 오늘 누적 등락을 한눈에. 일봉은 미표시(분봉만). - render_svg_chart(intraday=) → meta.dayOpen(첫 봉 시가, 분봉만) 주입 - 분봉 엔드포인트 intraday=True - _setOhlcText: 당일시가대비 라인 + 팝업 박스/닫기버튼 동적 리사이즈 Co-Authored-By: Claude Opus 4.8 --- agents/stock/workspace/scripts/behive_web.py | 10 +++++-- .../stock/workspace/scripts/stock_analysis.py | 26 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/agents/stock/workspace/scripts/behive_web.py b/agents/stock/workspace/scripts/behive_web.py index ef81292..9b3a462 100644 --- a/agents/stock/workspace/scripts/behive_web.py +++ b/agents/stock/workspace/scripts/behive_web.py @@ -7317,8 +7317,14 @@ def render_html() -> str: 'xTipTxt.appendChild(_mkTspan({"font-size":24,"font-weight":500},_pctStr(c.l,c.o),lcol));' 'xTipTxt.appendChild(_mkTspan({x:16,dy:34,"font-size":28,"font-weight":700},"종 "+c.c.toLocaleString()+" ",ccol));' 'xTipTxt.appendChild(_mkTspan({"font-size":24,"font-weight":700},_pctStr(c.c,c.o),ccol));' + # 분봉: 당일 시가(첫 봉) 대비 등락률 — 그 시점까지의 오늘 누적 등락. + 'var hasDO=meta.dayOpen&&meta.dayOpen>0;' + 'if(hasDO){xTipTxt.appendChild(_mkTspan({x:16,dy:32,"font-size":23,"font-weight":600},"당일시가대비 "+_pctStr(c.c,meta.dayOpen),_col(c.c,meta.dayOpen)));}' # 팝업 위치 — vline 의 반대 쪽으로 자동 배치 (좌/우). 화면 벗어남 방지 clamp. - 'var tipW=320,tipH=176,gap=10;' + 'var tipW=340,tipH=hasDO?212:176,gap=10;' + # 당일시가 라인 유무에 따라 배경 박스·닫기버튼 위치 동적 조정. + 'xTipBg.setAttribute("width",tipW);xTipBg.setAttribute("height",tipH);' + 'closeG.setAttribute("transform","translate("+(tipW-20)+",20)");' 'var tipX=xPos+gap;' 'if(tipX+tipW>meta.chartW-meta.padR)tipX=xPos-gap-tipW;' 'if(tipX str: +def render_svg_chart(snap: dict, range_key: str = '1Y', ref_price: float | None = None, intraday: bool = False) -> str: """캔들 + SMA 3선 + 거래량 막대 인라인 SVG. range_key: '1Y' (250일) | '6M' (120일) | '1M' (21일). 같은 snapshot 데이터에서 슬라이스만. + ref_price: 기준가(전일종가 등) — 지정 시 노란 점선 기준선 + 마지막 종가의 대비 등락률 표시 (분봉용). snap: collect_snapshot 결과. candles_asc·sma5/20/60 사용. 외부 JS 의존성 0. CSS는 inline. """ @@ -570,6 +571,9 @@ def render_svg_chart(snap: dict, range_key: str = '1Y') -> str: sma_vals = [v for v in (sma5 + sma20 + sma60) if v is not None] y_max = max(max(highs), max(sma_vals) if sma_vals else 0) y_min = min(min(lows), min(sma_vals) if sma_vals else float('inf')) + if ref_price: + y_max = max(y_max, ref_price) # 기준선이 항상 보이게 스케일에 포함 + y_min = min(y_min, ref_price) if y_min == y_max: y_max = y_min + 1 # 위·아래 패딩 5% @@ -607,6 +611,8 @@ def render_svg_chart(snap: dict, range_key: str = '1Y') -> str: ], 'ymin': y_min, 'ymax': y_max, + # 분봉일 때만 당일 시가(첫 봉 시가) — 크로스헤어 툴팁의 '당일시가 대비 등락률'용. + 'dayOpen': (candles[0]['open'] if (intraday and candles) else 0), 'priceTop': PRICE_TOP, 'priceBot': PRICE_BOT, 'volTop': VOL_TOP, @@ -696,6 +702,24 @@ def render_svg_chart(snap: dict, range_key: str = '1Y') -> str: parts.append(sma_path(sma20, '#34d399', 'SMA20')) # 초록 parts.append(sma_path(sma60, '#a78bfa', 'SMA60')) # 보라 + # ---- 전일종가 기준선 + 대비 등락률 (ref_price 지정 시 — 분봉) ---- + if ref_price: + ry = price_y(ref_price) + parts.append( + f'' + ) + parts.append( + f'전일' + ) + last_close = candles[-1]['close'] + pct = (last_close / ref_price - 1) * 100 + pc = '#ef4444' if pct > 0 else '#3b82f6' if pct < 0 else '#9ca3af' # 한국 관습: 상승=빨강 + parts.append( + f'{last_close:,} 전일대비 {pct:+.2f}%' + ) + # SMA 범례 legend_y = PRICE_TOP + 14 legend_items = [('SMA5', '#fbbf24'), ('SMA20', '#34d399'), ('SMA60', '#a78bfa')]