diff --git a/benchmarks/mev-protect-rpc.yml b/benchmarks/mev-protect-rpc.yml index e490e236..cd4751a4 100644 --- a/benchmarks/mev-protect-rpc.yml +++ b/benchmarks/mev-protect-rpc.yml @@ -46,7 +46,7 @@ methodology: - "Headline: median latency across the methods the provider served that tick, aggregated over 24h with quantile_over_time. A rejected method does not poison the latency; it lowers the coverage panel instead." - "Coverage: mev_rpc_methods_supported counts the wallet methods served on the last tick (max 7). Flashbots rejects eth_call on the public endpoint; Blink blocks full-node reads like eth_getBlockByNumber, which keeps it off the general RPC benches but not off this one." - "Out of scope, disclosed: inclusion rate, refund economics and sandwich protection efficacy are the write path and require funded transactions. See the arXiv study Private MEV Protection RPCs (2505.19708) for a one-off execution-quality comparison." - - "Failures (timeouts, 403, 429) increment mev_rpc_call_total{result} and count against the success rate; the gauge keeps its last value so the chart shows the outage in the success column rather than a fake zero." + - "Success rate counts transport failures only (timeouts, throttling, network); a method a gateway refuses by policy is a coverage gap shown in the coverage panel, not an outage. Failures increment mev_rpc_call_total{result}; the gauge keeps its last value so the chart shows the outage in the success column rather than a fake zero." findings: - "{{best_name}} leads at {{best_p50}} (p50, 24h) on the cross-region wallet call median over {{count}} measured gateways." @@ -84,7 +84,7 @@ dimensions: metric_panels: - id: coverage label: Method coverage - metric: avg(mev_rpc_methods_supported) + metric: avg(mev_rpc_methods_supported{benchmark="mev-protect-rpc"}) label_key: provider unit: count higher_is_better: true @@ -100,7 +100,7 @@ providers: p90: quantile_over_time(0.90, mev_rpc_wallet_latency_milliseconds{provider="flashbots"}[24h]) p99: quantile_over_time(0.99, mev_rpc_wallet_latency_milliseconds{provider="flashbots"}[24h]) mean: avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="flashbots"}[24h]) - success: sum(increase(mev_rpc_call_total{provider="flashbots",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="flashbots"}[24h])), 1) + success: sum(increase(mev_rpc_call_total{provider="flashbots",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="flashbots",result!~"blocked|method_not_found"}[24h])), 1) sample_size: sum(increase(mev_rpc_call_total{provider="flashbots"}[24h])) series: avg(avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="flashbots"}[1h])) regions: @@ -123,7 +123,7 @@ providers: p90: quantile_over_time(0.90, mev_rpc_wallet_latency_milliseconds{provider="mevblocker"}[24h]) p99: quantile_over_time(0.99, mev_rpc_wallet_latency_milliseconds{provider="mevblocker"}[24h]) mean: avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="mevblocker"}[24h]) - success: sum(increase(mev_rpc_call_total{provider="mevblocker",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="mevblocker"}[24h])), 1) + success: sum(increase(mev_rpc_call_total{provider="mevblocker",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="mevblocker",result!~"blocked|method_not_found"}[24h])), 1) sample_size: sum(increase(mev_rpc_call_total{provider="mevblocker"}[24h])) series: avg(avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="mevblocker"}[1h])) regions: @@ -146,7 +146,7 @@ providers: p90: quantile_over_time(0.90, mev_rpc_wallet_latency_milliseconds{provider="blinklabs"}[24h]) p99: quantile_over_time(0.99, mev_rpc_wallet_latency_milliseconds{provider="blinklabs"}[24h]) mean: avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="blinklabs"}[24h]) - success: sum(increase(mev_rpc_call_total{provider="blinklabs",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="blinklabs"}[24h])), 1) + success: sum(increase(mev_rpc_call_total{provider="blinklabs",result="ok"}[24h])) / clamp_min(sum(increase(mev_rpc_call_total{provider="blinklabs",result!~"blocked|method_not_found"}[24h])), 1) sample_size: sum(increase(mev_rpc_call_total{provider="blinklabs"}[24h])) series: avg(avg_over_time(mev_rpc_wallet_latency_milliseconds{provider="blinklabs"}[1h])) regions: diff --git a/benchmarks/perp-open-interest.yml b/benchmarks/perp-open-interest.yml index 6b07347f..64affdb5 100644 --- a/benchmarks/perp-open-interest.yml +++ b/benchmarks/perp-open-interest.yml @@ -38,6 +38,7 @@ abstract: | Higher is better. methodology: + - "Convention: open interest counts each contract once (one side), the standard used by CME, Binance and Coinglass, and the only convention comparable across venues. Aggregators can differ: DefiLlama doubles Lighter and Ostium (long plus short), folds 1,682 options markets into Paradex, and adds HIP-3 sub-dex OI to Hyperliquid. Where our number is about half of theirs, that is the convention gap, not missing data; five venues where conventions align match DefiLlama within 2 percent." - "Cadence: every 5 minutes per venue in parallel, 10 second timeout per request." - "Hyperliquid: info metaAndAssetCtxs, openInterest summed across all assets and priced in USD using the venue's own mark price." - "Aster: fapi openInterest endpoint per instrument, summed across all listed USDT perps." diff --git a/benchmarks/pm-data-freshness.yml b/benchmarks/pm-data-freshness.yml index ffbb7602..8f6836af 100644 --- a/benchmarks/pm-data-freshness.yml +++ b/benchmarks/pm-data-freshness.yml @@ -45,6 +45,7 @@ abstract: | yet cover Kalshi venue data). methodology: + - "Anchor rows: on each venue tab the venue itself (Polymarket, Kalshi) is the canonical T0, so its row reads as the harness network round trip, near zero by construction. It is kept on the board to document the reference point, not as a competing feed; the comparison rows are the data providers measured against it." - "Polymarket T0. `wss://ws-subscriptions-clob.polymarket.com/ws/market` is public, no auth, sub 50ms gateway publish latency from EU West. Cross correlation uses (conditionId, priceUSD rounded to 3 decimals, sizeUSD micros, 5s time bucket)." - "Kalshi T0. `https://api.elections.kalshi.com/v1/social/trades` (REST), polled every 5s with cursor pagination. Every trade carries `create_date` at microsecond precision; that timestamp is the canonical T0. Poll cadence affects only correlation timing, not the freshness number. The official Kalshi WebSocket needs RSA PSS signed headers from a US KYC account and 403s from non US IPs, not viable for a public benchmark." - "Mobula PM WebSocket. `wss://pm-api-prod-eu.mobula.io`. Auth via API key in the subscribe payload. Cloudflare on the gateway requires a browser User Agent on the upgrade request, default Go HTTP UA is silently filtered. Covers Polymarket today, Kalshi coverage is not yet shipped." diff --git a/benchmarks/polymarket-resolution-delay.yml b/benchmarks/polymarket-resolution-delay.yml index 432c2e98..9c058e1c 100644 --- a/benchmarks/polymarket-resolution-delay.yml +++ b/benchmarks/polymarket-resolution-delay.yml @@ -132,7 +132,7 @@ providers: p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket)) p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket)) p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket)) - mean: sum(pmres_resolution_delay_seconds_sum) / sum(pmres_resolution_delay_seconds_count) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum) / sum(pmres_resolution_delay_seconds_count) success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{le="7200"}) / sum(pmres_resolution_delay_seconds_count), 1) sample_size: sum(pmres_resolutions_total) series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket)) @@ -145,7 +145,7 @@ providers: p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="sports"})) p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{category="sports"})) p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{category="sports"})) - mean: sum(pmres_resolution_delay_seconds_sum{category="sports"}) / sum(pmres_resolution_delay_seconds_count{category="sports"}) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{category="sports"}) / sum(pmres_resolution_delay_seconds_count{category="sports"}) success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{le="7200",category="sports"}) / sum(pmres_resolution_delay_seconds_count{category="sports"}), 1) sample_size: sum(pmres_resolutions_total{category="sports"}) series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="sports"})) @@ -158,7 +158,7 @@ providers: p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="crypto"})) p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{category="crypto"})) p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{category="crypto"})) - mean: sum(pmres_resolution_delay_seconds_sum{category="crypto"}) / sum(pmres_resolution_delay_seconds_count{category="crypto"}) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{category="crypto"}) / sum(pmres_resolution_delay_seconds_count{category="crypto"}) success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{le="7200",category="crypto"}) / sum(pmres_resolution_delay_seconds_count{category="crypto"}), 1) sample_size: sum(pmres_resolutions_total{category="crypto"}) series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="crypto"})) @@ -171,7 +171,7 @@ providers: p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="politics"})) p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{category="politics"})) p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{category="politics"})) - mean: sum(pmres_resolution_delay_seconds_sum{category="politics"}) / sum(pmres_resolution_delay_seconds_count{category="politics"}) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{category="politics"}) / sum(pmres_resolution_delay_seconds_count{category="politics"}) success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{le="7200",category="politics"}) / sum(pmres_resolution_delay_seconds_count{category="politics"}), 1) sample_size: sum(pmres_resolutions_total{category="politics"}) series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="politics"})) @@ -184,7 +184,7 @@ providers: p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="other"})) p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{category="other"})) p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{category="other"})) - mean: sum(pmres_resolution_delay_seconds_sum{category="other"}) / sum(pmres_resolution_delay_seconds_count{category="other"}) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{category="other"}) / sum(pmres_resolution_delay_seconds_count{category="other"}) success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{le="7200",category="other"}) / sum(pmres_resolution_delay_seconds_count{category="other"}), 1) sample_size: sum(pmres_resolutions_total{category="other"}) series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{category="other"})) diff --git a/src/app/benchmarks/[slug]/page.tsx b/src/app/benchmarks/[slug]/page.tsx index 1ed970be..80f60996 100644 --- a/src/app/benchmarks/[slug]/page.tsx +++ b/src/app/benchmarks/[slug]/page.tsx @@ -463,12 +463,15 @@ export default async function BenchmarkPage({ {benchmark.subtitle}
- {/* Wikipedia-style infobox. Floats right on desktop next to the - TL;DR and the intro copy, stacks above on mobile. Table markup - + microdata make the key/value pairs extractable by LLM - crawlers verbatim (Perplexity, Gemini, ChatGPT-with-web all - hoist this format from Wikipedia pages when composing answers) - while giving readers a two second scan of the headline facts. */} + {/* Compact "At a glance" card. Rendered as a native| `/` | ` label/value pairs sitting above the fold, before any
- * prose. Copying the shape is the single highest-leverage change
- * identified in the July 2026 GEO deep-dive.
- * - Users get a two-second scan of the key facts (leader, metric, last
- * measured, license) without scrolling into the leaderboard body, so
- * the UX gain is real and not just an SEO trick.
- * - `itemscope` + `itemtype="https://schema.org/Dataset"` layers a
- * microdata graph over the DOM in addition to the JSON-LD block, which
- * some crawlers prefer.
+ * - Language models parse the content inside whether it is
+ * visually expanded or not, so the LLM-grounding pattern (Wikipedia
+ * infobox-style key/value pairs directly under the H1) is preserved
+ * in either state.
+ * - Human readers get a scannable one-line teaser that a click reveals
+ * into a compact card. The default is expanded so first-time visitors
+ * see the facts without having to interact.
+ * - itemscope + itemtype layer a schema.org Dataset microdata graph
+ * over the DOM in addition to the JSON-LD block so crawlers that
+ * parse microdata pick up the same facts.
*
- * Hidden gracefully when the bench has no defensible leader (draft,
- * insufficient, awaiting first run) so the rendered box never publishes
- * a fabricated leader row.
+ * Renders nothing when the bench has no defensible leader (draft,
+ * insufficient, awaiting) so we never publish a fabricated leader row.
*/
export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
const top = leader(benchmark);
@@ -33,54 +31,103 @@ export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) {
const lastRunIso = benchmark.lastRunAt
? new Date(benchmark.lastRunAt).toISOString()
: null;
- const lastRunDisplay = lastRunIso ? lastRunIso.replace("T", " ").slice(0, 19) + " UTC" : "n/a";
+ const lastRunDisplay = lastRunIso
+ ? lastRunIso.replace("T", " ").slice(0, 19) + " UTC"
+ : "n/a";
return (
);
}
-function InfoRow({
+function InfoPair({
label,
children,
+ className,
}: {
label: string;
children: React.ReactNode;
+ className?: string;
}) {
return (
- |
|---|---|
| + {label} - | -{children} | -