// EquityFactor.jsx — 주식 리스크 요인 분석: 국가 → 섹터 → 종목 탑다운 기여 분해.
// 색상 헬퍼
function efCountryColor(c, i) { return COUNTRY_HUE[c] || PAL[i % PAL.length]; }
// ── 탑다운 요인 분석 섹션 (주식 포트폴리오 관리 내) ──
function EquityFactorSection({ pf }) {
const [country, setCountry] = React.useState('전체');
const m = summarize(pf);
const hc = React.useMemo(() => holdContribs(pf), [pf]);
const byCountry = React.useMemo(() => groupContribs(pf, (u) => u.country), [pf]);
React.useEffect(() => { if (country !== '전체' && !byCountry.some((c) => c.key === country)) setCountry('전체'); }, [pf]);
const scoped = country === '전체' ? hc : hc.filter((r) => r.country === country);
const scopedAmt = scoped.reduce((s, r) => s + r.amt, 0);
// 섹터별 (선택 국가 범위 내)
const bySector = React.useMemo(() => {
const g = {};
scoped.forEach((r) => { if (!g[r.sector]) g[r.sector] = { key: r.sector, amt: 0, w: 0 }; g[r.sector].amt += r.amt; g[r.sector].w += r.w; });
return Object.values(g).sort((a, b) => b.amt - a.amt);
}, [pf, country]);
return (
{/* ① 국가별 리스크 현황 */}
① 국가별 리스크 기여도
전체 위험액 {Math.round(m.risk).toLocaleString()}억
{byCountry.map((c, i) =>
)}
{byCountry.map((c, i) => { const on = country === c.key;
return
; })}
{/* ② 섹터별 + ③ 종목별 */}
② 섹터별 리스크 기여도
{country === '전체' ? '전체 국가' : country + ' 범위'}
({ label: s.key, value: Math.round(s.amt), color: SECTOR_HUE[s.key] || T.blue, sub: `비중 ${s.w.toFixed(1)}% · 기여 ${(s.amt / (scopedAmt || 1) * 100).toFixed(1)}%` }))} unit="억" />
③ 종목별 리스크 기여도
{scoped.length}종목 · 기여 위험액 {Math.round(scopedAmt).toLocaleString()}억
({ ...r, _key: r.id }))}
cols={[
{ k: 'name', t: '종목', w: '24%', render: (r) => {r.name} },
{ k: 'country', t: '국가', w: 62, render: (r) => {r.country} },
{ k: 'sector', t: '섹터', render: (r) => {r.sector} },
{ k: 'w', t: '비중', num: true, render: (r) => {r.w.toFixed(1)}%, sortVal: (r) => r.w },
{ k: 'amt', t: '기여 위험액', num: true, render: (r) => {Math.round(r.amt).toLocaleString()}억, sortVal: (r) => r.amt },
{ k: 'frac', t: '기여율', num: true, render: (r) => 0.2 ? T.alert : r.frac > 0.12 ? T.warn : T.c400} w={52} />{(r.frac * 100).toFixed(1)}%, sortVal: (r) => r.frac },
]} />
);
}
// ── 모니터링(주식) 탭용 하이라이트: 국가·섹터·종목 Top 기여 요약 ──
function EquityFactorHighlight({ onMore }) {
const pf = React.useMemo(() => firmPortfolio('주식'), []);
const m = summarize(pf);
const byCountry = groupContribs(pf, (u) => u.country).slice(0, 3);
const bySector = groupContribs(pf, (u) => u.sector).slice(0, 3);
const byStock = holdContribs(pf).slice(0, 3);
const Col = ({ title, rows, colorOf }) => (
{title}
{rows.map((r, i) =>
{i + 1}
{r.key}
{(r.frac * 100).toFixed(0)}%
)}
);
return (
주식 포트폴리오에서 상세 분석} />
efCountryColor(r.key, i)} />
SECTOR_HUE[r.key] || T.blue} />
({ key: r.name, frac: r.frac, sector: r.sector }))} colorOf={(r) => SECTOR_HUE[r.sector] || T.blue} />
데스크 주식 위험액 (CVaR)
{Math.round(m.risk).toLocaleString()}
억
평가액 {fmtEok(pf.value)} · 담당 {peopleOf('주식').length}명
);
}
Object.assign(window, { EquityFactorSection, EquityFactorHighlight, efCountryColor });