// BondFactor.jsx — 채권 리스크 요인 분석: 기준금리 · 국가/등급 스프레드 · DV01 · 기간구조 · Greeks · 팩터 익스포저.
const bfMn = (v) => Math.round(v * 100).toLocaleString(); // 억 → 백만원
const bfSigned = (v) => (v > 0 ? '+' : v < 0 ? '−' : '') + Math.abs(Math.round(v * 100)).toLocaleString();
// ── ① 나라별 기준금리 ──
function PolicyRates() {
return (
{SPREAD_GRADES.map((g) =>
{g}
)}
{SPREAD_MATRIX.map((row) =>
{row.country}
{held.has(row.country) && }
{SPREAD_GRADES.map((g) => { const v = row.spreads[g]; const d = row.dod[g]; const dark = v / mx > 0.55;
return
{v}bp
0 ? T.up : T.c400 }}>{d > 0 ? '+' : ''}{d}bp
; })}
)}
보유 포지션이 있는 국가 · 국채 대비 OAS, 전일대비
);
}
// ── ③ DV01 (포지션별 · 넷) ──
function DV01Panel({ pf }) {
const s = dv01Summary(pf);
const firm = firmDV01();
const mx = Math.max(...s.rows.map((r) => r.dv01), 0.001);
const kpis = [
{ l: 'Gross DV01', v: bfMn(s.gross), sub: '보유 포지션 합', c: T.c900 },
{ l: '선물 헤지', v: bfSigned(s.hedge), sub: '국채선물 매도', c: T.down },
{ l: 'Net DV01', v: bfMn(s.net), sub: '금리 +1bp 시 평가손', c: T.blue },
{ l: '전사 Net DV01', v: bfMn(firm.net), sub: `채권 데스크 전체 (${peopleOf('채권').length}명)`, c: T.c900 },
];
return (
{/* 기간구조 */}
기간구조별 DV01 (백만원/bp)
{tenors.map((t) => (
0 ? T.c900 : T.c300 }}>{t.dv01 > 0 ? bfMn(t.dv01) : '—'}
0.66 ? T.blue : t.dv01 > 0 ? '#7FA8D9' : T.c200, borderRadius: '6px 6px 3px 3px', transition: 'height .3s' }} />
{t.label}
{t.n}종목
))}
{/* Greeks */}
Greeks (감마·베가)
포트 감마 (Convexity)
{bfMn(gammaSum)} 백만/(100bp)²
금리 대변동 시 손익 곡률
포트 베가 (콜 내재)
0 ? T.warn : T.c900, marginTop: 5, textAlign: 'left' }}>{bfMn(vegaSum)} 백만/vol 1%p
콜러블 채권 {vegaRows.length}종목 보유
{gk.slice(0, 5).map((r, i) =>
{r.name}
{r.call &&
콜러블}
Γ {bfMn(r.gamma)}
0 ? T.warn : T.c300, width: 58, textAlign: 'right' }}>ν {r.vega > 0 ? bfMn(r.vega) : '—'}
)}
);
}
// ── ⑤ 팩터별 리스크 익스포저 (관리 액션 포함) ──
function BondFactorExposure({ pf }) {
const dec = factorDecompose(pf).filter((d) => d.id !== 'eq');
return (
{dec.map((d, i) =>
{d.name}
{d.code}
{d.id !== 'vol' ? {d.sensLabel} → {fvEok(d.sensAmt)} : '요인 설명 후 잔차'}
{Math.round(d.amt).toLocaleString()}억
{(d.frac * 100).toFixed(1)}%
)}
);
}
// ── 섹션 조립 (채권 포트폴리오 관리 내) ──
function BondFactorSection({ pf }) {
return (
);
}
// ── 모니터링(채권) 탭용 하이라이트 ──
function BondFactorHighlight({ onMore }) {
const pf = React.useMemo(() => firmPortfolio('채권'), []);
const m = summarize(pf);
const s = dv01Summary(pf);
const tenors = tenorDV01(pf); const topTenor = tenors.slice().sort((a, b) => b.dv01 - a.dv01)[0];
const dec = factorDecompose(pf).filter((d) => d.id !== 'eq').slice(0, 2);
const kr = POLICY_RATES[0]; const us = POLICY_RATES[1];
const Stat = ({ l, v, u, sub, c }) => (
);
return (
채권 포트폴리오에서 상세 분석} />
팩터 기여 Top
{dec.map((d, i) =>
{d.name}
{Math.round(d.amt).toLocaleString()}억 · {(d.frac * 100).toFixed(0)}%
)}
);
}
Object.assign(window, { BondFactorSection, BondFactorHighlight, PolicyRates, SpreadMatrix, DV01Panel, TenorGreeks, BondFactorExposure, bfMn, bfSigned });