From 3f347217c69763943d0d0053e2ab35b6fea69773 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Wed, 24 Jun 2026 16:10:32 +0200 Subject: [PATCH] fix(perps): seed cohort venues in provider index + funding-stability cohort copy - src/lib/providers.ts: seed stub profiles for 9 perp cohort venues (drift, vertex, edgex, extended, aevo, pacifica, variational, ostium, grvt) that have no bench appearance. Without this, getProvider() returned undefined and /products/ rendered 404 for every venue the cohort harness tracks but no bench specifically ranks. - benchmarks/perp-funding-stability.yml: fix cohort copy to honestly describe the Mobula funding aggregator scope (HL onchain + 11 CEX), not the onchain-perp cohort the original draft promised. - src/data/provider-registry.ts: vertex URL updated to www.nado.xyz after the rebrand (vertexprotocol.com returns 404). - src/app/perps/page.tsx: description says 16 perpetual DEXes, not 15 (off-by-one). --- benchmarks/perp-funding-stability.yml | 4 +-- src/app/perps/page.tsx | 2 +- src/data/provider-registry.ts | 2 +- src/lib/providers.ts | 38 +++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/benchmarks/perp-funding-stability.yml b/benchmarks/perp-funding-stability.yml index 2664079b..c8bcec0d 100644 --- a/benchmarks/perp-funding-stability.yml +++ b/benchmarks/perp-funding-stability.yml @@ -4,7 +4,7 @@ slug: perp-funding-stability number: "043" title: Perp DEX funding stability, 7-day stddev of ETH funding ranked seo_title: "Perp DEX funding stability 2026: ETH funding stddev across 12 venues ranked" -seo_description: "Live 7-day standard deviation of ETH perpetual futures funding rate in bps across 12 major venues. Lower means more stable funding for carry traders." +seo_description: "Live 7-day standard deviation of ETH perpetual futures funding rate in bps across 12 major venues (Hyperliquid plus 11 CEX-style perp books). Lower means more stable funding for carry traders." subtitle: 7-day standard deviation of ETH funding rate, in basis points per 24h, across the Mobula funding cohort. Lower means funding stays in a tight band, the carry trader's preferred signal. category: Trading status: live @@ -44,7 +44,7 @@ methodology: - "Source. Same upstream as perp-funding: each venue's public funding endpoint polled every 60 seconds, normalized to bps per hour and per 24h hold, sign preserved." - "Metric. stddev_over_time(perp_venue_funding_24h_bps{asset=\"ETH\"}[7d]) per venue. The 7d window smooths intraday noise but reacts to regime shifts within a week." - "Sign handling. The underlying series is signed (positive means longs pay). Stability measures the swing, so the ranking uses the unsigned stddev. Average direction and level are the perp-funding bench." - - "Cohort. 12 venues: Hyperliquid, Binance, Bybit, OKX, dYdX v4, Paradex, Aster on the CEX-plus-orderbook side; Drift, Vertex, EdgeX, Extended and Aevo on the onchain-perp side. All quote ETH funding with documented intervals." + - "Cohort. 12 venues from the Mobula funding aggregator: Hyperliquid (onchain), Binance, Bybit, OKX, Gate, Lighter, KuCoin, MEXC, Bitget, Kraken, Coinbase, Deribit. All publish normalized hourly ETH funding." - "Cadence. The upstream funding harness polls every 60 seconds; Prometheus scrapes every 30 seconds. The 7 day stddev refreshes on every scrape." - "Failures. A venue whose upstream funding feed errors keeps its last gauge value; the perp_venue_health gauge drops to 0 and the leaderboard tags the row stale." - "Reproducibility. The harness emits perp_venue_funding_24h_bps with labels venue and asset; the bench query is the stddev over 7d on that series with asset=ETH pinned." diff --git a/src/app/perps/page.tsx b/src/app/perps/page.tsx index 0a095b27..6f6cd00e 100644 --- a/src/app/perps/page.tsx +++ b/src/app/perps/page.tsx @@ -24,7 +24,7 @@ import { SITE } from "@/data/site"; */ const DESCRIPTION = - "Live volume, open interest, fees, all-in cost and funding rate across 15 perpetual DEXes. Reproducible methodology, refreshed every minute, sources public."; + "Live volume, open interest, fees, all-in cost and funding rate across 16 perpetual DEXes. Reproducible methodology, refreshed every minute, sources public."; export const metadata: import("next").Metadata = pageMetadata({ path: "/perps", diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index b44e625a..a31ac75c 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -1304,7 +1304,7 @@ export const PROVIDER_REGISTRY: Record = { twitter: "@DriftProtocol", }, vertex: { - url: "https://vertexprotocol.com", + url: "https://www.nado.xyz", description: "Arbitrum perp and spot orderbook DEX with off-chain matching and on-chain settlement. Migrating to Ink Foundation.", twitter: "@vertex_protocol", diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 26d12eb6..867f0ffb 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -344,6 +344,29 @@ async function buildProviders(): Promise { }); } + // Seed stub profiles for cohort venues that don't yet have a bench + // appearance. Without this, /products/ renders 404 for every + // venue the harness tracks but no bench specifically ranks (e.g. the + // perp cohort venues drift, edgex, extended, aevo, pacifica, + // variational, ostium, grvt have no row in perp-fees or perp-funding, + // so getProvider() returned undefined). The stub carries name + type + // only; appearances stays empty so the product page still renders the + // PerpVenueSection from the cohort harness without falsely claiming + // bench coverage. PM cohort venues stay unaffected because every PM + // venue already appears in at least one PM bench. + for (const v of PERP_VENUE_SEED) { + const key = v.slug.toLowerCase(); + if (byKey.has(key)) continue; + byKey.set(key, { + slug: v.slug, + name: v.name, + type: "protocol", + appearances: [], + wins: 0, + categories: [], + }); + } + const profiles = Array.from(byKey.values()); profiles.sort((a, b) => { if (a.wins !== b.wins) return b.wins - a.wins; @@ -355,6 +378,21 @@ async function buildProviders(): Promise { return profiles; } +// Cohort venues that should have a /products/ page even when no +// bench in benchmarks/ ranks them. Imported as a flat list here to +// avoid a build-time cycle with the perp-stats module. +const PERP_VENUE_SEED = [ + { slug: "drift", name: "Drift" }, + { slug: "vertex", name: "Vertex" }, + { slug: "edgex", name: "EdgeX" }, + { slug: "extended", name: "Extended" }, + { slug: "aevo", name: "Aevo" }, + { slug: "pacifica", name: "Pacifica" }, + { slug: "variational", name: "Variational" }, + { slug: "ostium", name: "Ostium" }, + { slug: "grvt", name: "GRVT" }, +]; + /** Cross-request cache. The expensive part isn't `getBenchmarks()` * itself (already wrapped in unstable_cache) but the per-provider * ranking + wins + per-chain aggregation post-processing on top. A 60