// Labels and helpers for Source Mesh v2: observation types, comparability classes, roles, coverage tiers. import type { Comparability, CoverageStatus, CoverageTier, ObservationType } from "./types"; export const OBSERVATION_TYPE_LABEL: Record = { TRADE: "Trade", MID: "Mid", QUOTE: "Quote", INDEX_VALUE: "Index value", INDICATIVE: "Indicative", STABLECOIN_PROXY: "Stablecoin proxy", DERIVED: "Derived", OFFICIAL_FIX: "Official fix", REFERENCE_RATE: "Reference rate", SETTLEMENT: "Settlement", EOD_CLOSE: "EOD close", NAV: "NAV", }; export const COMPARABILITY_LABEL: Record = { LIVE: "live market consensus", FIX: "official fixing", EOD: "end-of-day close", }; export const COMPARABILITY_OF: Record = { TRADE: "LIVE", MID: "LIVE", QUOTE: "LIVE", INDEX_VALUE: "LIVE", INDICATIVE: "LIVE", STABLECOIN_PROXY: "LIVE", DERIVED: "LIVE", OFFICIAL_FIX: "FIX", REFERENCE_RATE: "FIX", SETTLEMENT: "EOD", EOD_CLOSE: "EOD", NAV: "EOD", }; export const PROXY_TYPES = new Set(["STABLECOIN_PROXY", "DERIVED", "INDICATIVE"]); /** Readable copy for exclusion reasons. `canonical` is the comparability class of the canonical value. */ export function reasonLabel(reason: string | null | undefined, type?: ObservationType, canonical?: Comparability | null): string { switch (reason) { case "stale": return "stale — outside its freshness window"; case "not_comparable": { const own = type ? COMPARABILITY_LABEL[COMPARABILITY_OF[type]] : null; const can = canonical ? COMPARABILITY_LABEL[canonical] : null; return own && can ? `not comparable: ${own} vs ${can}` : "not comparable with the canonical class"; } case "temporal_mismatch": return "temporal mismatch — too far from the freshest observation"; case "validation_only": return "validation only — restricted rights, never votes"; case "validator_disagrees": return "validator disagrees with the consensus"; case "outlier": return "outlier — more than 2 % from the median"; case "superseded_by_live": return "superseded by a live source"; default: return reason ?? "excluded"; } } export type Role = "votes" | "confirms" | "validates" | "excluded"; export function roleOf(c: { included: boolean; reason: string | null; observation_type?: ObservationType; rights_status: string }): Role { if (c.included) return c.observation_type && PROXY_TYPES.has(c.observation_type) ? "confirms" : "votes"; if (c.reason === "validation_only" || c.reason === "validator_disagrees") return "validates"; return "excluded"; } export const ROLE_LABEL: Record = { votes: "votes", confirms: "confirms (proxy)", validates: "validates", excluded: "excluded" }; export const TIER_LABEL: Record = { A: "Tier A · major", B: "Tier B · liquid", C: "Tier C · long tail", D: "Tier D · official reference" }; export const TIER_TARGET: Record = { A: "≥ 3 independent families, 5 observations", B: "≥ 2 families, 3 observations", C: "1 family, 2 observations", D: "one official source is the truth" }; export const COVERAGE_STATUS_LABEL: Record = { COVERED: "covered", UNDERCOVERED: "under-covered", SINGLE_SOURCE: "single source", NO_DATA: "no data" }; export const COVERAGE_STATUS_TONE: Record = { COVERED: "bg-positive-soft text-positive", UNDERCOVERED: "bg-warning-soft text-warning", SINGLE_SOURCE: "bg-warning-soft text-warning", NO_DATA: "bg-stale-soft text-stale", }; export function formatDeltaBps(v: number | null | undefined): string { if (v == null || !Number.isFinite(v)) return "—"; const a = Math.abs(v); const s = a < 10 ? a.toFixed(2) : a < 100 ? a.toFixed(1) : Math.round(a).toString(); return `${v > 0 ? "+" : v < 0 ? "−" : ""}${s} bp`; } export function deltaTone(v: number | null | undefined): string { if (v == null) return "text-ink-3"; const a = Math.abs(v); return a <= 5 ? "text-positive" : a <= 25 ? "text-ink-2" : a <= 100 ? "text-warning" : "text-negative"; }