spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1import type { Metadata } from "next";2import Link from "next/link";3import { RedundancyPanel } from "@/components/market/redundancy-panel";4import { Empty, Page, PageHeader, Pill, Section, Stat } from "@/components/ui/section";5import { api } from "@/lib/api";6import { COVERAGE_STATUS_LABEL, COVERAGE_STATUS_TONE, TIER_LABEL, TIER_TARGET } from "@/lib/coverage";7import { ASSET_CLASS_LABEL, cx, instrumentHref } from "@/lib/format";8import type { CoverageReport, CoverageTier } from "@/lib/types";910export const metadata: Metadata = {11 title: "Source coverage",12 description: "How many independent source families observe each instrument, redundancy targets by tier, the source-expansion queue and statistically inferred shared upstreams.",13 alternates: { canonical: "/coverage" },14};15export const dynamic = "force-dynamic";1617const TIERS: CoverageTier[] = ["A", "B", "C", "D"];18const CLASSES = ["EQUITY", "ETF", "INDEX", "CRYPTO", "FOREX", "COMMODITY", "FUTURE", "TREASURY", "BOND", "INTEREST_RATE"];1920export default async function CoveragePage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {21 const sp = await searchParams;22 const tier = typeof sp.tier === "string" && TIERS.includes(sp.tier as CoverageTier) ? (sp.tier as CoverageTier) : undefined;23 const cls = typeof sp.asset_class === "string" && CLASSES.includes(sp.asset_class) ? sp.asset_class : undefined;24 const qs = new URLSearchParams({ limit: "200" });25 if (tier) qs.set("tier", tier);26 if (cls) qs.set("asset_class", cls);27 const [report, global] = await Promise.all([api<CoverageReport>(`/v1/coverage?${qs}`), tier || cls ? api<CoverageReport>("/v1/coverage?limit=1") : null]);28 const s = report.summary;29 const g = (global ?? report).summary;30 const href = (t?: CoverageTier, c?: string) => {31 const p = new URLSearchParams();32 if (t) p.set("tier", t);33 if (c) p.set("asset_class", c);34 const q = p.toString();35 return `/coverage${q ? `?${q}` : ""}`;36 };37 const byClass = Object.entries(s.byAssetClass).sort((a, b) => b[1].instruments - a[1].instruments);38 return (39 <Page wide>40 <PageHeader41 kicker="Source Mesh"42 title="Source coverage"43 lead="One instrument → many observers → one canonical market state. Market Atlas does not trust a quote: it observes the market through independent paths, counts source families (not URLs), and publishes the consensus with the evidence. This page measures that redundancy and lists where it is still thin."44 actions={45 <Link href="/methodology#source-mesh" className="text-sm text-accent hover:underline">46 How it is computed →47 </Link>48 }49 />50 <div className="grid grid-cols-1 gap-6 lg:grid-cols-[1.2fr_1fr]">51 <RedundancyPanel coverage={g} prominent />52 <div className="rounded-md border border-rule bg-surface p-4">53 <h3 className="text-[11px] font-medium uppercase tracking-wide text-ink-3">Redundancy targets by tier</h3>54 <div className="mt-2 divide-y divide-rule">55 {TIERS.map((t) => {56 const tt = g.tiers[t];57 return (58 <div key={t} className="grid grid-cols-[auto_1fr_auto] items-center gap-3 py-2 text-sm">59 <Link href={href(t, cls)} className={cx("mono inline-flex h-7 w-7 items-center justify-center rounded border text-xs font-semibold", tier === t ? "border-ink bg-ink text-canvas" : "border-rule text-ink-2 hover:border-rule-strong")}>60 {t}61 </Link>62 <div className="min-w-0">63 <div className="font-medium">{TIER_LABEL[t].replace(/^Tier [A-D] · /, "")}</div>64 <div className="truncate text-[11.5px] text-ink-3">{report.targets[t] ?? TIER_TARGET[t]}</div>65 <div className="mt-1 h-1 overflow-hidden rounded-full bg-surface-3">66 <span className={cx("block h-full rounded-full", tt.attainment >= 90 ? "bg-positive" : tt.attainment >= 50 ? "bg-accent" : "bg-warning")} style={{ width: `${Math.min(100, tt.attainment)}%` }} />67 </div>68 </div>69 <div className="text-right">70 <div className="mono tnum">71 {tt.covered} / {tt.instruments}72 </div>73 <div className="text-[11px] text-ink-3">{tt.attainment.toFixed(1)}% covered</div>74 </div>75 </div>76 );77 })}78 </div>79 </div>80 </div>8182 <Section title="By asset class" hint="quoted instruments · share observed by ≥ 2 families · mean independent families">83 <div className="overflow-x-auto rounded-md border border-rule bg-surface">84 <table className="table-dense">85 <thead>86 <tr>87 <th>Asset class</th>88 <th className="text-right">Quoted</th>89 <th className="text-right">Multi-source</th>90 <th className="text-right">Share</th>91 <th className="text-right">Mean families</th>92 <th />93 </tr>94 </thead>95 <tbody>96 {byClass.map(([k, v]) => (97 <tr key={k}>98 <td className="font-medium">{ASSET_CLASS_LABEL[k] ?? k}</td>99 <td className="num">{v.instruments}</td>100 <td className="num">{v.multiSource}</td>101 <td className="num">102 <span className="inline-flex items-center gap-2">103 <span className="inline-block h-1.5 w-16 overflow-hidden rounded-full bg-surface-3">104 <span className={cx("block h-full rounded-full", v.instruments && v.multiSource / v.instruments >= 0.9 ? "bg-positive" : v.multiSource ? "bg-accent" : "bg-stale")} style={{ width: `${v.instruments ? (v.multiSource / v.instruments) * 100 : 0}%` }} />105 </span>106 {v.instruments ? `${Math.round((v.multiSource / v.instruments) * 100)}%` : "—"}107 </span>108 </td>109 <td className="num">{v.meanFamilies.toFixed(2)}</td>110 <td className="text-right">111 <Link href={href(tier, k)} className="text-xs text-accent hover:underline">112 queue →113 </Link>114 </td>115 </tr>116 ))}117 </tbody>118 </table>119 </div>120 </Section>121122 <Section123 title="Source expansion queue"124 hint="instruments below their redundancy target, most valuable gaps first (tier weight × missing families)"125 badge={126 <span className="mono rounded bg-surface-3 px-1.5 py-0.5 text-[10.5px] text-ink-2 tnum">127 {report.queue.length}128 {report.queue.length >= 200 ? "+" : ""}129 </span>130 }131 >132 <div className="mb-3 flex flex-wrap items-center gap-2">133 <span className="text-xs text-ink-3">Tier</span>134 <Pill href={href(undefined, cls)} active={!tier}>135 all136 </Pill>137 {TIERS.map((t) => (138 <Pill key={t} href={href(t, cls)} active={tier === t}>139 {t}140 </Pill>141 ))}142 <span className="ml-2 text-xs text-ink-3">Class</span>143 <Pill href={href(tier)} active={!cls}>144 all145 </Pill>146 {CLASSES.filter((c) => s.byAssetClass[c] || c === cls).map((c) => (147 <Pill key={c} href={href(tier, c)} active={cls === c}>148 {ASSET_CLASS_LABEL[c] ?? c}149 </Pill>150 ))}151 </div>152 {report.queue.length ? (153 <div className="overflow-x-auto rounded-md border border-rule bg-surface">154 <table className="table-dense">155 <thead>156 <tr>157 <th>#</th>158 <th>Instrument</th>159 <th>Tier</th>160 <th className="text-right">Families / target</th>161 <th className="text-right">Missing</th>162 <th className="text-right">Observations</th>163 <th className="text-right">Proxies · validators</th>164 <th>Class</th>165 <th>Status</th>166 <th className="text-right">Score</th>167 </tr>168 </thead>169 <tbody>170 {report.queue.map((q, i) => (171 <tr key={q.instrumentId}>172 <td className="mono text-ink-3">{i + 1}</td>173 <td>174 <Link href={instrumentHref(q.instrumentId)} className="hover:underline">175 <span className="mono font-medium">{q.symbol}</span>176 <span className="block max-w-[260px] truncate text-[11.5px] text-ink-3">{q.name}</span>177 </Link>178 </td>179 <td>180 <span className="mono rounded border border-rule px-1.5 text-xs" title={TIER_TARGET[q.tier]}>181 {q.tier}182 </span>183 </td>184 <td className="num">185 {q.families} / {q.target.families}186 </td>187 <td className="num text-warning">{q.gap > 0 ? `−${q.gap}` : "—"}</td>188 <td className="num">{q.observations}</td>189 <td className="num text-ink-2">190 {q.proxies} · {q.validators}191 </td>192 <td className="text-xs text-ink-2">193 {ASSET_CLASS_LABEL[q.assetClass] ?? q.assetClass}194 {q.comparability ? <span className="block text-[10.5px] text-ink-3">{q.comparability.toLowerCase()} class</span> : null}195 </td>196 <td>197 <span className={cx("inline-flex h-5 items-center rounded-[3px] px-1.5 text-[10.5px] font-medium uppercase tracking-wide", COVERAGE_STATUS_TONE[q.status])}>{COVERAGE_STATUS_LABEL[q.status]}</span>198 </td>199 <td className="num">{Math.round(q.score * 100)}</td>200 </tr>201 ))}202 </tbody>203 </table>204 </div>205 ) : (206 <Empty>Every instrument in this selection meets its redundancy target.</Empty>207 )}208 <p className="mt-2 text-xs text-ink-3">209 A <em>family</em> is a group of sources believed to share an upstream (declared, or inferred statistically). Proxies (stablecoin markets, crosses) and validators (restricted-rights sources) confirm a value but never count as independent families. Score = 65 % family attainment + 35 % observation attainment.210 </p>211 </Section>212213 <Section title="Likely shared upstreams" hint="pairs of sources whose values coincide within 2 s on ≥ 92 % of ≥ 120 aligned samples — counted as one family">214 {report.lineage.length ? (215 <div className="overflow-x-auto rounded-md border border-rule bg-surface">216 <table className="table-dense">217 <thead>218 <tr>219 <th>Source A</th>220 <th>Source B</th>221 <th className="text-right">Similarity</th>222 <th className="text-right">Aligned samples</th>223 <th className="text-right">Instruments</th>224 </tr>225 </thead>226 <tbody>227 {report.lineage.map((p) => (228 <tr key={`${p.sourceA}|${p.sourceB}`}>229 <td>230 <Link href={`/sources#${p.sourceA}`} className="mono hover:underline">231 {p.sourceA}232 </Link>233 </td>234 <td>235 <Link href={`/sources#${p.sourceB}`} className="mono hover:underline">236 {p.sourceB}237 </Link>238 </td>239 <td className="num">{(p.similarity * 100).toFixed(1)}%</td>240 <td className="num">{p.samples.toLocaleString("en-US")}</td>241 <td className="num">{p.instruments}</td>242 </tr>243 ))}244 </tbody>245 </table>246 </div>247 ) : (248 <Empty>No pair of sources currently looks like a shared upstream. The lineage engine recomputes every five minutes from live observations.</Empty>249 )}250 </Section>251252 <div className="mt-8 grid grid-cols-2 gap-4 border-t border-rule pt-6 sm:grid-cols-4">253 <Stat label="Quoted instruments" value={g.quoted.toLocaleString("en-US")} />254 <Stat label="≥ 3 families" value={g.byFamilies[">=3"]} sub="Tier-A target" />255 <Stat label="≥ 5 families" value={g.byFamilies[">=5"]} sub="most observed" />256 <Stat label="No fresh source" value={g.byFamilies["0"]} tone={g.byFamilies["0"] ? "warn" : "muted"} sub="last known value shown as stale" />257 </div>258 </Page>259 );260}261