diff --git a/agents/stock/workspace/scripts/behive_web.py b/agents/stock/workspace/scripts/behive_web.py
index 7113f2f..311c4f7 100644
--- a/agents/stock/workspace/scripts/behive_web.py
+++ b/agents/stock/workspace/scripts/behive_web.py
@@ -4439,10 +4439,14 @@ dl.pending-detail .pending-line-meta { margin-top: 2px; }
.info-tabbtn:hover { color: #d6d8dd; }
.info-tabbtn.active { color: #f0f0f0; border-bottom-color: #ff4d5e; }
.info-tabpanel[hidden] { display: none; }
-/* 탭 상단 내용 요약 박스 */
-.info-summary { background: #11161f; border: 1px solid #232a36; border-left: 3px solid #4a8cf0; border-radius: 8px; padding: 9px 11px; margin: 0 0 12px; }
-.info-summary-t { font-size: 11px; color: #7a8493; font-weight: 600; margin-bottom: 4px; }
-.info-summary-b { font-size: 13px; color: #cfd5df; line-height: 1.75; }
+/* 탭 상단 해석 박스 — 좋은 점 / 주의할 점 */
+.info-summary { background: #11161f; border: 1px solid #232a36; border-radius: 8px; padding: 10px 12px; margin: 0 0 12px; }
+.info-jd + .info-jd { margin-top: 8px; }
+.info-jd > b { display: block; font-size: 12px; margin-bottom: 3px; }
+.info-jd-g > b { color: #34d399; }
+.info-jd-c > b { color: #fbbf24; }
+.info-summary ul { margin: 0; padding-left: 17px; }
+.info-summary li { font-size: 12.5px; color: #cfd5df; line-height: 1.65; margin: 1px 0; }
.info-q { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0; margin-left: 4px; border-radius: 50%; border: 1px solid rgba(125,211,168,0.4); background: rgba(125,211,168,0.08); color: #7dd3a8; font-size: 10px; font-weight: 700; line-height: 1; cursor: pointer; font-family: inherit; vertical-align: middle; }
.info-q:hover { background: rgba(125,211,168,0.2); border-color: #7dd3a8; }
.info-q:active { transform: translateY(1px); }
@@ -7888,29 +7892,37 @@ def render_html() -> str:
'if(j.reports&&j.reports.length){var r=j.reports[0];var ar="";if(r.target_action&&r.target_action.indexOf("상향")>=0)ar=\' ▲\';else if(r.target_action&&r.target_action.indexOf("하향")>=0)ar=\' ▼\';h+=\'
최신 리포트
\'+escHtml(r.date||"")+\' \'+escHtml(r.broker||"")+\' \'+(r.target?\'목표 \'+escHtml(r.target)+ar+\'\':"")+\'
\'+escHtml(r.title||"")+\'
\';}'
'return h;'
'}'
- # 탭 상단 내용 요약 박스 — 핵심 수치를 한 묶음으로 + 간단 해석(규칙 기반, 데이터 그대로).
- 'function infoSummaryBox(inner){return inner?(\'\'):"";}'
- 'function companySummary(j){'
- 'var L=[],per=Number(j.per),pbr=Number(j.pbr),roe=Number(j.roe),val=[];'
- 'if(j.per!=null&&j.per!==""&&!isNaN(per))val.push("PER "+per.toFixed(2)+"배");'
- 'if(j.pbr!=null&&j.pbr!==""&&!isNaN(pbr))val.push("PBR "+pbr.toFixed(2)+"배");'
- 'if(val.length)L.push("밸류에이션 "+val.join(" · "));'
- 'if(j.roe!=null&&j.roe!==""&&!isNaN(roe)){var tag=roe<0?"적자":(roe>=15?"우수":(roe>=8?"양호":"다소 낮음"));L.push("수익성 ROE "+roe.toFixed(1)+"% ("+tag+")");}'
- 'return infoSummaryBox(L.join("
"));'
+ # 탭 상단 해석 박스 — 지표를 ✅좋은 점 / ⚠️주의할 점으로 판단(규칙 기반). 특이점 없으면 미표시.
+ 'function _gnum(v){var n=Number(v);return (v==null||v===""||isNaN(n))?null:n;}'
+ 'function judgeBox(good,caution){'
+ 'if(!good.length&&!caution.length)return "";'
+ 'var h="";'
+ 'if(good.length)h+=\'✅ 좋은 점\'+good.map(function(x){return "- "+x+"
";}).join("")+\'
\';'
+ 'if(caution.length)h+=\'⚠️ 주의할 점\'+caution.map(function(x){return "- "+x+"
";}).join("")+\'
\';'
+ 'return \'\'+h+\'
\';'
'}'
- 'function growthSummary(j){'
- 'var gr=(j.fundamentals&&j.fundamentals.growth)||{},fc=(j.fundamentals&&j.fundamentals.consensus)||{},cn=j.consensus||{},L=[],g=[];'
- 'if(gr.sales_yoy!=null)g.push("매출 "+growthDisp(gr.sales_yoy));'
- 'if(gr.oper_profit_yoy!=null)g.push("영익 "+growthDisp(gr.oper_profit_yoy));'
- 'if(gr.eps_yoy!=null)g.push("EPS "+growthDisp(gr.eps_yoy));'
- 'if(g.length)L.push("성장(YoY) "+g.join(" · "));'
- 'if(fc.target_price!=null){var p=Number(j.price),tp=Number(fc.target_price),up="";if(p&&tp&&!isNaN(p)&&!isNaN(tp))up=" (상승여력 "+growthDisp((tp/p-1)*100)+")";L.push("목표주가 "+fmtInt(fc.target_price)+"원"+up+(fc.opinion_label?" · "+escHtml(fc.opinion_label):""));}'
- 'if(cn.target_change_pct!=null)L.push("목표가 리비전(3M) "+growthDisp(cn.target_change_pct));'
- 'if(cn.surprise_op!=null)L.push("영익 서프라이즈 "+growthDisp(cn.surprise_op));'
- 'return infoSummaryBox(L.join("
"));'
+ 'function companyJudge(j){'
+ 'var good=[],caution=[],per=_gnum(j.per),pbr=_gnum(j.pbr),roe=_gnum(j.roe);'
+ 'if(roe!=null){if(roe<0)caution.push("ROE "+roe.toFixed(1)+"% — 자기자본이 손실(수익성 부진)");else if(roe>=15)good.push("ROE "+roe.toFixed(1)+"% — 자본 수익성 우수");else if(roe<8)caution.push("ROE "+roe.toFixed(1)+"% — 자본 수익성 다소 낮음");}'
+ 'if(per!=null){if(per<=0)caution.push("PER 산정 불가 — 적자 또는 이익 미미");else if(per>=30)caution.push("PER "+per.toFixed(1)+"배 — 기대가 선반영된 고평가 부담");else if(per<=10)good.push("PER "+per.toFixed(1)+"배 — 밸류에이션 부담이 작음");}'
+ 'if(pbr!=null){if(pbr>=3)caution.push("PBR "+pbr.toFixed(1)+"배 — 자산가치 대비 비싼 편");else if(pbr>0&&pbr<1)good.push("PBR "+pbr.toFixed(1)+"배 — 자산가치(청산가치) 이하");}'
+ 'return judgeBox(good,caution);'
'}'
- 'var companyInner=companySummary(j)+titled("가치지표",secGrid("가치지표"))+titled("기본정보",secGrid("기본정보"));'
- 'var growthInner=growthSummary(j)+secGrid("성장성·컨센서스");'
+ 'function growthJudge(j){'
+ 'var gr=(j.fundamentals&&j.fundamentals.growth)||{},fc=(j.fundamentals&&j.fundamentals.consensus)||{},cn=j.consensus||{},good=[],caution=[];'
+ 'var sy=_gnum(gr.sales_yoy),oy=_gnum(gr.oper_profit_yoy),ey=_gnum(gr.eps_yoy);'
+ 'if(oy!=null){if(oy>=20)good.push("영업이익 +"+oy.toFixed(0)+"% — 이익 고성장");else if(oy<0)caution.push("영업이익 "+oy.toFixed(0)+"% — 이익 역성장");}'
+ 'if(sy!=null){if(sy<0)caution.push("매출 "+sy.toFixed(0)+"% — 외형 역성장");else if(sy>=15)good.push("매출 +"+sy.toFixed(0)+"% — 외형 성장 견조");}'
+ 'if(ey!=null&&ey<0)caution.push("EPS "+ey.toFixed(0)+"% — 주당순이익 감소");'
+ 'var p=_gnum(j.price),tp=_gnum(fc.target_price);'
+ 'if(p&&tp){var up=(tp/p-1)*100;if(up>=15)good.push("목표주가까지 상승여력 +"+up.toFixed(0)+"%");else if(up<0)caution.push("현재가가 목표주가를 "+(-up).toFixed(0)+"% 웃돔 — 추가 상승여력 제한");}'
+ 'if(fc.opinion_label){if(fc.opinion_label.indexOf("매수")>=0)good.push("증권사 투자의견 "+escHtml(fc.opinion_label));else if(fc.opinion_label.indexOf("매도")>=0||fc.opinion_label.indexOf("중립")>=0||fc.opinion_label.indexOf("보유")>=0)caution.push("투자의견 "+escHtml(fc.opinion_label)+" — 적극 매수는 아님");}'
+ 'var tr=_gnum(cn.target_change_pct);if(tr!=null){if(tr>0)good.push("최근 3개월 목표가 상향(+"+tr.toFixed(1)+"%) — 전망 개선");else if(tr<0)caution.push("최근 3개월 목표가 하향("+tr.toFixed(1)+"%) — 전망 악화");}'
+ 'var so=_gnum(cn.surprise_op);if(so!=null){if(so>0)good.push("최근 영업이익 어닝 서프라이즈(+"+so.toFixed(1)+"%)");else if(so<0)caution.push("최근 영업이익이 컨센서스 하회("+so.toFixed(1)+"%)");}'
+ 'return judgeBox(good,caution);'
+ '}'
+ 'var companyInner=companyJudge(j)+titled("가치지표",secGrid("가치지표"))+titled("기본정보",secGrid("기본정보"));'
+ 'var growthInner=growthJudge(j)+secGrid("성장성·컨센서스");'
'var repInner=infoReportsHtml(j);'
'var TB=[["summary","요약",sumPanel()],["company","기업정보·가치",companyInner],["growth","성장성·컨센서스",growthInner],["reports","투자리포트",repInner]];'
'var bar=\'\';var pans="";'