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?(\'
📝 요약
\'+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+=\'
✅ 좋은 점
\';' + 'if(caution.length)h+=\'
⚠️ 주의할 점
\';' + '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="";'