SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
3.7 KB · 55 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import Link from "next/link";3import { InstrumentTable } from "@/components/market/instrument-table";4import { Page, PageHeader, Section, Stat } from "@/components/ui/section";5import { api } from "@/lib/api";6import type { MarketsOverview } from "@/lib/types";78export const metadata: Metadata = { title: "Markets overview", description: "Indices, equities, crypto, forex, rates and commodities — one canonical, multi-source view.", alternates: { canonical: "/markets" } };9export const dynamic = "force-dynamic";1011export default async function MarketsPage() {12  const m = await api<MarketsOverview>("/v1/markets");13  const open = m.exchanges.filter((e) => e.status.state === "OPEN");14  const b = m.breadth;15  return (16    <Page wide>17      <PageHeader kicker="Markets" title="Global overview" lead="Canonical quotes across asset classes. Each table streams live where a real-time source exists; otherwise values show their delayed, at-close or end-of-day status." />18      <div className="grid grid-cols-2 gap-4 rounded-md border border-rule bg-surface px-4 py-4 sm:grid-cols-4">19        <Stat label="Venues open now" value={`${open.length} / ${m.exchanges.length}`} sub={open.slice(0, 3).map((e) => e.name.split(" ")[0]).join(", ") || "none trading"} />20        <Stat label="US equity breadth" value={b.instruments ? `${b.advancers} ▲ ${b.decliners} ▼` : "—"} sub={b.instruments ? `${b.instruments} quoted · median ${b.median_change_percent?.toFixed(2)}%` : "no session data"} tone={b.advancers > b.decliners ? "pos" : b.decliners > b.advancers ? "neg" : undefined} />21        <Stat label="Session highs / lows" value={`${b.new_session_highs} / ${b.new_session_lows}`} sub="live instruments at extremes" />22        <Stat label="Sections" value="7" sub="indices · stocks · ETFs · crypto · FX · rates · commodities" />23      </div>24      <Section title="Indices" href="/indices" hint="15-min delayed (Cboe) · at close outside the session">25        <InstrumentTable rows={m.indices} compact showExchange={false} />26      </Section>27      <div className="grid grid-cols-1 [&>*]:min-w-0 gap-6 lg:grid-cols-2">28        <Section title="Top gainers · US equities" href="/stocks?sort=-change">29          <InstrumentTable rows={m.gainers} compact />30        </Section>31        <Section title="Top losers · US equities" href="/stocks?sort=change">32          <InstrumentTable rows={m.losers} compact defaultSort="change" />33        </Section>34      </div>35      <Section title="Crypto" href="/crypto" hint="real-time consensus of public venue feeds">36        <InstrumentTable rows={m.crypto} liveClass="CRYPTO" compact showExchange />37      </Section>38      <div className="grid grid-cols-1 [&>*]:min-w-0 gap-6 lg:grid-cols-2">39        <Section title="Forex" href="/forex" hint="official reference rates">40          <InstrumentTable rows={m.forex} compact showExchange={false} defaultSort="symbol" />41        </Section>42        <Section title="Rates & yields" href="/rates" hint="percent · end of day">43          <InstrumentTable rows={m.rates} compact showExchange={false} defaultSort="symbol" />44        </Section>45      </div>46      <Section title="Commodities & futures" href="/commodities" hint="continuous front month · settlement">47        <InstrumentTable rows={m.commodities} compact showExchange={false} defaultSort="symbol" />48      </Section>49      <p className="mt-6 text-xs text-ink-3">50        Prices are aggregations of independent observations. Read the <Link href="/methodology" className="text-accent hover:underline">methodology</Link> and the <Link href="/licensing" className="text-accent hover:underline">data rights</Link> page for what each status means.51      </p>52    </Page>53  );54}55