feat: 기업정보 모달 가치/성장성 탭 요약을 해석형(좋은점/주의점)으로
수치 나열 대신 지표를 ✅좋은 점 / ⚠️주의할 점으로 판단: - 가치: ROE(우수/낮음/적자)·PER(고평가 부담/부담 작음)·PBR(비쌈/청산가치 이하) - 성장: 매출·영익·EPS 성장/역성장·상승여력·투자의견·목표가 리비전·서프라이즈 규칙 기반·표준 임계치, 특이점 없으면 미표시. (이전 나열식 요약 대체) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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=\' <span style="color:#ef4444">▲</span>\';else if(r.target_action&&r.target_action.indexOf("하향")>=0)ar=\' <span style="color:#3b82f6">▼</span>\';h+=\'<div class="info-sec-title">최신 리포트</div><div style="font-size:12px;color:#9aa4b2"><span style="color:#7a8493">\'+escHtml(r.date||"")+\' \'+escHtml(r.broker||"")+\'</span> \'+(r.target?\'<span style="color:#fbbf24">목표 \'+escHtml(r.target)+ar+\'</span>\':"")+\'</div><div style="font-size:13px;color:#fff;margin-top:2px">\'+escHtml(r.title||"")+\'</div>\';}'
|
||||
'return h;'
|
||||
'}'
|
||||
# 탭 상단 내용 요약 박스 — 핵심 수치를 한 묶음으로 + 간단 해석(규칙 기반, 데이터 그대로).
|
||||
'function infoSummaryBox(inner){return inner?(\'<div class="info-summary"><div class="info-summary-t">📝 요약</div><div class="info-summary-b">\'+inner+\'</div></div>\'):"";}'
|
||||
'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)+"% <span class=\\"muted small\\">("+tag+")</span>");}'
|
||||
'return infoSummaryBox(L.join("<br>"));'
|
||||
# 탭 상단 해석 박스 — 지표를 ✅좋은 점 / ⚠️주의할 점으로 판단(규칙 기반). 특이점 없으면 미표시.
|
||||
'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+=\'<div class="info-jd info-jd-g"><b>✅ 좋은 점</b><ul>\'+good.map(function(x){return "<li>"+x+"</li>";}).join("")+\'</ul></div>\';'
|
||||
'if(caution.length)h+=\'<div class="info-jd info-jd-c"><b>⚠️ 주의할 점</b><ul>\'+caution.map(function(x){return "<li>"+x+"</li>";}).join("")+\'</ul></div>\';'
|
||||
'return \'<div class="info-summary">\'+h+\'</div>\';'
|
||||
'}'
|
||||
'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("<br>"));'
|
||||
'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=\'<div class="info-tabs" role="tablist">\';var pans="";'
|
||||
|
||||
Reference in New Issue
Block a user