spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1import Link from "next/link";2import { ChangesPanel } from "@/components/market/changes-panel";3import { HealthPanel } from "@/components/market/health-panel";4import { InstrumentTable } from "@/components/market/instrument-table";5import { LiveEvents } from "@/components/market/live-events";6import { PulseRow } from "@/components/market/pulse-grid";7import { RedundancyPanel } from "@/components/market/redundancy-panel";8import { LiveTape } from "@/components/market/tape";9import { TelemetryStrip } from "@/components/market/telemetry-strip";10import { WorldMap } from "@/components/market/world-map";11import { SearchButton } from "@/components/layout/search-dialog";12import { Section } from "@/components/ui/section";13import { api, apiOptional } from "@/lib/api";14import type { Changes, DataHealth, MarketEvent, MarketsOverview, Stats } from "@/lib/types";1516export const dynamic = "force-dynamic";1718export default async function HomePage() {19 const [stats, markets, events, health, changes] = await Promise.all([20 api<Stats>("/v1/stats"),21 api<MarketsOverview>("/v1/markets"),22 apiOptional<MarketEvent[]>("/v1/events?limit=30"),23 apiOptional<DataHealth>("/v1/data-health"),24 apiOptional<Changes>("/v1/changes?window=1h"),25 ]);26 const movers = [...markets.gainers.slice(0, 5), ...markets.losers.slice(0, 5)];27 return (28 <div className="mx-auto max-w-[1440px] px-3 sm:px-5">29 {/* Hero */}30 <section className="pb-6 pt-8 sm:pt-12">31 <p className="text-[11px] font-medium uppercase tracking-[0.14em] text-ink-3">Market Atlas</p>32 <h1 className="mt-2 max-w-3xl text-3xl font-semibold leading-[1.05] tracking-tight sm:text-5xl">The live map of global markets.</h1>33 <p className="mt-3 max-w-2xl text-[15px] text-ink-2 sm:text-base">34 Thousands of instruments. Independent sources compared in real time. One continuously updating, provenance-backed market graph — every quote carries its freshness, its sources and its confidence.35 </p>36 <div className="mt-5 flex flex-wrap items-center gap-2">37 <SearchButton className="h-12 !text-[15px] sm:min-w-[360px]" />38 <Link href="/live" className="inline-flex h-12 items-center gap-2 rounded-md bg-ink px-4 text-sm font-medium text-canvas hover:opacity-90">39 <span className="live-dot inline-block h-2 w-2 rounded-full bg-positive" /> Open the live feed40 </Link>41 <Link href="/methodology" className="inline-flex h-12 items-center px-3 text-sm text-ink-2 hover:text-ink">42 How prices are formed →43 </Link>44 </div>45 <TelemetryStrip initial={stats} className="mt-7" />46 </section>4748 {/* Pulse */}49 <Section title="Global market pulse" hint="canonical quotes · live where sources allow, delayed or end-of-day otherwise" href="/markets">50 <PulseRow title="Indices" href="/indices" items={markets.indices} />51 <PulseRow title="Crypto" href="/crypto" items={markets.crypto} />52 <PulseRow title="Forex" href="/forex" items={markets.forex} />53 <PulseRow title="Rates & yields" href="/rates" items={markets.rates} />54 <PulseRow title="Commodities & futures" href="/commodities" items={markets.commodities} />55 <PulseRow title="Equities" href="/stocks" items={markets.equities} className="border-b-0" />56 </Section>5758 {/* Source redundancy — the mesh is the product */}59 {stats.coverage && (60 <div className="mt-8 grid grid-cols-1 [&>*]:min-w-0 gap-6 lg:grid-cols-[1fr_1.1fr]">61 <RedundancyPanel coverage={stats.coverage} prominent className="!mt-0" />62 <div className="flex flex-col justify-center rounded-md border border-rule bg-surface p-4 text-sm text-ink-2">63 <h3 className="text-[11px] font-medium uppercase tracking-wide text-ink-3">One instrument → many observers → one canonical state</h3>64 <p className="mt-2">Market Atlas does not trust a quote. Each instrument is observed through independent paths — exchange feeds, official publishers, public quote pages — and only comparable observations are compared: a live trade is never measured against an official fixing or last week's close.</p>65 <p className="mt-2">Real markets <span className="font-medium text-ink">vote</span>; stablecoin markets and crosses <span className="font-medium text-ink">confirm</span>; restricted-rights sources <span className="font-medium text-ink">validate</span> without ever setting or showing a value. Sources that statistically mirror each other are counted as one family.</p>66 <p className="mt-3 text-xs text-ink-3">67 Targets: tier A majors ≥ 3 independent families and 5 observations · tier B ≥ 2 / 3 · tier C 1 / 2 · tier D one official source. <Link href="/coverage" className="text-accent hover:underline">See the expansion queue →</Link>68 </p>69 </div>70 </div>71 )}7273 {/* Tape + events */}74 <div className="mt-8 grid grid-cols-1 [&>*]:min-w-0 gap-6 lg:grid-cols-[1.1fr_1fr]">75 <Section title="Live tape" hint="every canonical price change" className="!mt-0">76 <LiveTape />77 </Section>78 <Section title="Live events" hint="halts, filings, moves, source incidents" href="/events" className="!mt-0">79 <LiveEvents initial={events ?? []} max={18} dense className="max-h-[470px] overflow-y-auto" />80 </Section>81 </div>8283 {/* Map */}84 <Section title="World market map" hint="venues by session state · hover a marker for local time" href="/exchanges" hrefLabel="All exchanges">85 <WorldMap exchanges={markets.exchanges} />86 </Section>8788 {/* Movers + health + changes */}89 <div className="mt-8 grid grid-cols-1 [&>*]:min-w-0 gap-6 lg:grid-cols-[1.3fr_1fr]">90 <Section title="Largest moves · US equities" hint="Cboe delayed quotes, 15 min" href="/stocks" className="!mt-0">91 <InstrumentTable rows={movers} compact defaultSort="change" emptyText="No equity quotes yet — the US session has not produced observations." />92 {markets.breadth.instruments > 0 && (93 <p className="mt-2 text-xs text-ink-3">94 Breadth: <span className="text-positive">{markets.breadth.advancers} advancing</span> · <span className="text-negative">{markets.breadth.decliners} declining</span> · median {markets.breadth.median_change_percent?.toFixed(2)}% across {markets.breadth.instruments} quoted names.95 </p>96 )}97 </Section>98 <div className="space-y-4">99 {health && <HealthPanel health={health} />}100 {changes && <ChangesPanel changes={changes} />}101 </div>102 </div>103104 <section className="mt-12 grid grid-cols-1 [&>*]:min-w-0 gap-6 border-t border-rule pt-8 text-sm text-ink-2 md:grid-cols-3">105 <div>106 <h3 className="font-semibold text-ink">Multi-source by design</h3>107 <p className="mt-1">Prices are a weighted median of comparable, independent observations — one vote per upstream family, proxies and validators as confirmations. Open “Why this price?” on any instrument to see the full source coverage matrix.</p>108 </div>109 <div>110 <h3 className="font-semibold text-ink">Honest freshness</h3>111 <p className="mt-1">Live, delayed, at-close, end-of-day or stale — every value is labelled with its status and the age of its newest observation. Nothing stale is ever shown as live.</p>112 </div>113 <div>114 <h3 className="font-semibold text-ink">A growing memory</h3>115 <p className="mt-1">Every observation, event and filing is recorded with its provenance. The historical dataset grows every second the atlas runs.</p>116 </div>117 </section>118 </div>119 );120}121