import type { TickerOverview, ValuationResponse } from "@/types/api"; import { fmtCurrency, fmtPct } from "@/lib/format"; type ValState = "idle" | "loading" | "ready" | "error"; type Props = { overview: TickerOverview; valuation: ValuationResponse | null; valState: ValState; }; function barPositions(price: number, iv: number): { leftPct: number; widthPct: number; pricePct: number; ivPct: number; isPremium: boolean; } { const padding = 0.1; const min = Math.min(price, iv) * (1 - padding); const max = Math.max(price, iv) * (1 + padding); const range = max - min; if (range === 0) return { leftPct: 50, widthPct: 0, pricePct: 50, ivPct: 50, isPremium: false }; const pricePct = ((price - min) / range) * 100; const ivPct = ((iv - min) / range) * 100; const leftPct = Math.min(pricePct, ivPct); const widthPct = Math.abs(pricePct - ivPct); return { leftPct, widthPct, pricePct, ivPct, isPremium: price > iv }; } export function PriceVsValueCard({ overview, valuation, valState }: Props) { if (valState === "idle" || valState === "loading" || valState === "error") return null; const dcf = valuation?.dcf; if (!dcf?.available || dcf.intrinsic_value_per_share == null) return null; const price = overview.quote.price ?? overview.range_52w.price; if (price == null) return null; const iv = dcf.intrinsic_value_per_share; const { leftPct, widthPct, pricePct, ivPct, isPremium } = barPositions(price, iv); const pct = ((price - iv) / iv) * 100; const pctLabel = isPremium ? `Trading at ${pct.toFixed(1)}% premium to DCF` : `Trading at ${Math.abs(pct).toFixed(1)}% discount to DCF`; const [leftLabel, rightLabel] = isPremium ? [`IV ${fmtCurrency(iv)}`, `Price ${fmtCurrency(price)}`] : [`Price ${fmtCurrency(price)}`, `IV ${fmtCurrency(iv)}`]; return (
Price vs Value

Intrinsic Value

{fmtCurrency(price)} {isPremium ? "↑" : "↓"} IV {fmtCurrency(iv)}
{leftLabel} {rightLabel}

{pctLabel} · WACC {fmtPct(dcf.wacc)} · {dcf.projection_years}yr {dcf.growth_rate_used != null ? ` · growth ${fmtPct(dcf.growth_rate_used)}` : ""}

); }