finance-tools/src/components/YieldCurve.tsx

86 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Tag } from "./ui";
import { AppIcon } from "./PixelIcons";
import type { MacroPayload } from "../lib/api";
/** 殖利率曲線小圖:留白充足、線條偏細,縮小時用 viewBox 等比縮放 */
export default function YieldCurve({ yc }: { yc?: MacroPayload["yieldCurve"] }) {
if (!yc?.yields?.length || !yc.maturities?.length)
return <p className="muted small"></p>;
const ys = yc.yields;
const labels = yc.maturities;
const min = Math.min(...ys, ...(yc.prevYields || []));
const max = Math.max(...ys, ...(yc.prevYields || []));
const span = max - min || 1;
const W = 360;
const H = 128;
const padL = 28;
const padR = 20;
const padT = 22;
const padB = 28;
const plotW = W - padL - padR;
const plotH = H - padT - padB;
const x = (i: number) => padL + (plotW * i) / Math.max(1, labels.length - 1);
const y = (v: number) => padT + plotH * (1 - (v - min) / span);
const path = (arr: number[]) => arr.map((v, i) => `${x(i)},${y(v)}`).join(" ");
const yTicks = 3;
const gridYs = Array.from({ length: yTicks + 1 }, (_, i) => padT + (plotH * i) / yTicks);
return (
<div className="chart-panel chart-panel--curve">
{yc.inverted && (
<div className="chart-panel-tag">
<Tag tone="down">
<span className="tag-icon-inline">
<AppIcon name="warning" size={14} framed={false} />
</span>
</Tag>
</div>
)}
<svg viewBox={`0 0 ${W} ${H}`} className="mini-chart-svg" role="img" aria-label="殖利率曲線">
{gridYs.map((gy, i) => (
<line
key={i}
x1={padL}
x2={W - padR}
y1={gy}
y2={gy}
stroke="rgba(231,198,107,.06)"
strokeWidth="1"
/>
))}
{yc.prevYields?.length === ys.length && (
<polyline
points={path(yc.prevYields)}
fill="none"
stroke="rgba(174,180,207,.35)"
strokeWidth="1.5"
strokeDasharray="4 4"
strokeLinecap="round"
/>
)}
<polyline
points={path(ys)}
fill="none"
stroke="#6fe0d0"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
{ys.map((v, i) => (
<g key={i}>
<circle cx={x(i)} cy={y(v)} r="2.5" fill="#e7c66b" stroke="#0c1024" strokeWidth="1" />
<text x={x(i)} y={H - 8} fontSize="8" fill="#8b92b0" textAnchor="middle" fontFamily="var(--sans)">
{labels[i]}
</text>
</g>
))}
</svg>
<div className="chart-caption"></div>
</div>
);
}