// 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 });