diff --git a/alternatives/bitquery.yml b/alternatives/bitquery.yml index 9178d1e4..b0e77236 100644 --- a/alternatives/bitquery.yml +++ b/alternatives/bitquery.yml @@ -2,7 +2,7 @@ slug: bitquery target_product: Bitquery target_url: https://bitquery.io description: GraphQL API for blockchain data, multi-chain -benchmark: network-coverage +benchmark: asset-registry-coverage intro: | Bitquery is a GraphQL surface on top of indexed onchain data, with the schema as the actual product: queries declare exactly the fields needed across transfers, trades, DEX events and account state, and the indexer fans those out across chains. Pricing is point-based, with each field, filter and join consuming from a monthly point budget rather than from a flat request quota, which means a single dashboard with many panels can hit the ceiling well before request volume looks high. Chain coverage is broad but uneven in depth: some networks expose full DEX trade indexing, others only basic transfers. The team usually compared next is the one whose chain list either covers a long-tail network Bitquery does not, or covers the same network with a flatter pricing model that does not penalise wide GraphQL fragments on a per-field basis. diff --git a/alternatives/coingecko.yml b/alternatives/coingecko.yml index 3b056eb6..40ecb3ec 100644 --- a/alternatives/coingecko.yml +++ b/alternatives/coingecko.yml @@ -2,7 +2,7 @@ slug: coingecko target_product: CoinGecko target_url: https://www.coingecko.com/en/api description: Crypto price + market data API, multi-chain -benchmark: network-coverage +benchmark: asset-registry-coverage intro: | CoinGecko is a coin-centric price and market data REST API built around a curated asset list (the `/coins` universe) rather than around on-chain events. Tokens are added through a listing process, prices land on the API after that process completes, and the free tier is famously throttled (around 30 requests per minute on the demo key) which pushes any real product onto a paid plan within days of integration. The on-chain DEX side ships as a separate `/onchain` namespace inherited from GeckoTerminal, with its own quotas. Teams usually leave CoinGecko at one of two points: when the launch cadence of the tokens they care about outpaces the listing pipeline, or when the per-minute cap on the free tier forces a migration before the product is monetised enough to justify the enterprise tier. diff --git a/alternatives/gains.yml b/alternatives/gains.yml new file mode 100644 index 00000000..61dfe7cf --- /dev/null +++ b/alternatives/gains.yml @@ -0,0 +1,16 @@ +slug: gains +target_product: gains.trade +target_url: https://gains.trade +description: Oracle-priced perpetuals on Arbitrum, Base, Polygon and ApeChain +benchmark: perp-fees + +intro: | + gains.trade v8 is an oracle-priced perpetuals venue running on Arbitrum, Base, Polygon and ApeChain. It settles against a GNS staking vault rather than a central orderbook, with the position open fee read directly on-chain from the fees contract and the spread term computed from half of the on-chain spreadP parameter. Because there is no orderbook, there is no price impact when executing against it: a $1M trade costs exactly the same number of basis points as a $1k trade. This benchmark measures the live all-in cost of opening a $1000 ETH long 10x position across major perpetual venues, every five minutes, by reading taker fees from each venue's own API and walking each orderbook for $1000 of buy-side notional. gains.trade is queried via eth_call on Base: pairs(N) gives the spreadP and feeIndex for each market, then fees(feeIndex) gives the open fee (totalPositionSizeFeeP). Values are in 1e10 precision per Gains v8 convention. The same harness queries Hyperliquid, Lighter, dYdX v4 and GMX v2. Funding rate is published separately because it accrues per hour held, not at open. + +seo_title: "gains.trade alternatives: live benchmark" +seo_description: Compare gains.trade against Hyperliquid, Lighter, dYdX, GMX v2 on live taker fee plus spread crossed at $1k, $100k and $1M notional, refreshed every five minutes. + +status: live + +positioning: | + gains.trade v8 uses a decentralized GNS staking vault rather than an orderbook. Traders open positions against the vault, priced against a Chainlink or Pyth oracle rather than a live book. The vault absorbs trader profits and receives trader losses, with GNS stakers backstopping it in exchange for a share of protocol fees. This oracle-driven model is architecturally distinct from CLOB venues: there is no maker side, no maker rebate to earn, and the fee schedule is a single flat percentage of position size. For the perp-fees axis, this has one important implication: a $1M trade costs the same basis points as a $1k trade, because there is no book to walk and no price impact to eat. The all-in cost at any size equals the open fee plus half of spreadP, both read live from on-chain. The venue covers 364 active markets across crypto, forex, tokenized stocks, commodities and stock indices, the widest multi-asset catalog among EVM perp DEXes. The typical use case is directional traders on mid-cap or exotic pairs that lack deep orderbook liquidity on any CLOB venue; teams running large block trades also benefit from the flat cost structure versus a deep-but-not-infinite orderbook. diff --git a/alternatives/the-graph.yml b/alternatives/the-graph.yml index bc815819..8ec3883b 100644 --- a/alternatives/the-graph.yml +++ b/alternatives/the-graph.yml @@ -2,7 +2,7 @@ slug: the-graph target_product: The Graph target_url: https://thegraph.com description: Decentralised indexing protocol with subgraph queries -benchmark: network-coverage +benchmark: asset-registry-coverage intro: | The Graph is a decentralised indexing protocol where each dataset (a subgraph) is written, deployed and queried independently rather than read off a pre-built schema. Queries are GQL against indexer nodes, billed in GRT, with a hosted gateway in front. The model means data shape is owned by whoever wrote the subgraph: a missing field, a stale index or a deprecated mapping is on the publisher, not on a central API team. Chain coverage tracks what indexers have chosen to support, which leans heavily EVM and lags on newer L2s and non-EVM networks until someone publishes a subgraph for them. The two reasons teams move off are the cost of running a private subgraph at production load, and the operational burden of debugging an indexer regression on a chain the public hosted service does not cover. diff --git a/answers/which-prediction-market-data-api-is-the-freshest.yml b/answers/which-prediction-market-data-api-is-the-freshest.yml deleted file mode 100644 index 856fe12d..00000000 --- a/answers/which-prediction-market-data-api-is-the-freshest.yml +++ /dev/null @@ -1,43 +0,0 @@ -slug: which-prediction-market-data-api-is-the-freshest -question: "Which prediction market data API publishes the freshest Polymarket data?" -short_answer: | - Polymarket's own CLOB WebSocket gateway is the canonical fastest source by definition (T0, sub-50ms from EU West) and is not a competing data provider. Among third-party data providers, Codex currently leads at {{p50:codex}} (p50, 24h) on the Polymarket tab, measured as lag from venue publish to provider relay; Codex indexes the onchain Polygon confirmation so its structural floor is roughly 2 seconds of block time. Live per-venue ranking at openchainbench.com/benchmarks/pm-data-freshness. - -benchmark: pm-data-freshness - -intro: | - Prediction markets produce the most time sensitive event stream in crypto. An election market settles in seconds, a sports book moves on every play, and the price tick a trading UI shows is only as fresh as the API behind it. Builders integrating Polymarket through a data provider rather than hitting the CLOB directly need to know how many milliseconds that provider adds between Polymarket publishing a trade and the provider relaying the same trade to its WebSocket subscribers. This page answers exactly that. The OpenChainBench pm-data-freshness harness holds two WebSocket subscribers in parallel: Polymarket's own CLOB gateway (the canonical T0) and Codex GraphQL subscriptions, both subscribed to the same basket of top 20 Polymarket markets by 24 h volume, refreshed every 5 minutes. Each trade is matched across providers by a tuple of conditionId, price rounded to 3 decimals, trade size and a 5 second time bucket; the per provider lag versus Polymarket's gateway publish time is recorded as a Prometheus histogram and the leaderboard ranks by p50 in milliseconds, lower is fresher. - -methodology: | - Two WebSocket clients run in parallel inside the harness on a Railway europe west 4 instance. Polymarket's own gateway (`wss://ws-subscriptions-clob.polymarket.com/ws/market`, public, no auth, sub 50 ms publish latency from EU West) is the canonical T0 by construction because nothing downstream can be faster than the source. Codex GraphQL (`wss://graph.codex.io/graphql` with the `graphql-transport-ws` subprotocol) runs an `onPredictionTradesCreated` firehose subscription filtered client side to the Polymarket protocol marketIds in the active basket. The cross correlation key is `(conditionId, priceUSD times 1000 rounded, sizeUSD times 1 million rounded, floor(trade_time / 5s))`; the 5 second bucket absorbs minor clock skew between gateways without merging unrelated trades. Histogram buckets run 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, 60000 ms. Providers that fail to relay a matched trade within 90 seconds are not counted toward p50, only toward their receive total, so a provider cannot look fresh on the leaderboard by silently dropping events; the success rate column surfaces that. - -limitations: - - "Polymarket's own gateway is the canonical T0 by construction. Its row on the leaderboard sits near zero because the harness compares everything else against it, not because Polymarket has solved network latency; the displayed sub millisecond value is a floor (vector(0.5)) so the row stays visible on the linear chart axis next to the multi second Codex value." - - "Codex indexes the on chain Polygon confirmation of each Polymarket trade, so its p50 includes Polygon block time (around 2 seconds) plus ingestion. The Codex path is the right one for on chain reconciliation or settlement workflows; for live trading UIs the off chain relay path is the only viable one. The two answer different questions." - - "Measurement is single region (Railway europe west 4). A provider whose edge gateway is closer to a US East client could post different deltas there; multi region requires running additional monitor instances of the freshness harness, which is on the roadmap." - - "The basket is the top 20 active Polymarket markets by 24 h volume, refreshed every 5 minutes. Long tail markets with sparse trade flow are not on the leaderboard because the cross correlation matcher needs enough overlapping events to produce a stable histogram per provider." - - "Kalshi and Limitless are separate exchanges with separate data feeds and a different ingestion path; this bench measures Polymarket as the underlying venue. A Kalshi specific tab is a candidate for a later phase but is not the current scope." - -faq: - - q: "Which Polymarket data API has the lowest latency right now?" - a: "{{best_name}} currently leads at {{best_p50}} (p50 over the last 24 hours), measured as wall clock time from Polymarket's own CLOB WebSocket publish to provider relay on the same trade. The leaderboard re sorts every minute on fresh Prometheus samples, so the answer reflects the actual measured lag on the active market basket, not a marketing claim. Codex's on chain Polygon indexer carries a structural floor near 2 seconds because it ingests block confirmations." - - q: "What does freshness delta mean for a prediction market API?" - a: "The bench connects to Polymarket's own CLOB WebSocket and to each provider's WebSocket simultaneously, subscribes to the same markets, and for every trade event records how many milliseconds the provider takes to relay the event after Polymarket itself publishes it. Lower is better. Polymarket's own gateway publish time is the canonical T0 because by construction nothing downstream can be faster than the source. The cross correlation key (conditionId, price, size, 5 s bucket) survives minor clock skew between gateways without merging unrelated trades." - - q: "Why not include Polymarket REST polling on this bench?" - a: "Freshness is a WebSocket question. REST polling at 1 second cadence would have a floor freshness near 500 ms (poll interval divided by 2) plus round trip time, dominated by how often you poll. The Polymarket gateway WebSocket exists for exactly this reason, to avoid that floor. Adding REST as a row would make the leaderboard noisy without changing the conclusion: WebSocket beats polling by definition for real time data." - - q: "Are these numbers comparable to Kalshi or Limitless?" - a: "Not directly. This benchmark measures Polymarket as the underlying venue since it is the largest and the one most providers proxy. Kalshi and Limitless are separate exchanges with separate data feeds, and providers that cover them often run a different ingestion path. A Kalshi specific tab is a candidate for a later phase. For now, treat the leaderboard as how fresh is your Polymarket data feed." - - q: "How does the cross correlation matcher avoid false positives?" - a: "The signature is (conditionId, priceUSD times 1000 rounded to 3 decimals, sizeUSD times 1 million rounded, floor(trade_time / 5 seconds)). Two unrelated trades would need to share the same conditionId, the same price to 3 decimals, the same size to 6 decimals, and land in the same 5 second window to collide. On the top 20 markets that combination is unique in practice; on a thinly traded long tail market it could in principle merge two trades, which is one reason the basket is restricted to the top 20 by volume." - - q: "How often is the leaderboard refreshed?" - a: "Continuously at the WebSocket level. Every trade event lands on the harness in real time and updates the freshness histogram; the page reads a rolling 24 h p50, p90 and p99 every minute. The market basket itself is refreshed every 5 minutes against `gamma-api.polymarket.com` so the leaderboard tracks current top markets rather than yesterday's hot list." - -related: - - which-prediction-market-has-the-strictest-rate-limits - - which-crypto-data-api-covers-the-most-blockchains - - which-crypto-price-api-is-the-fastest - - prediction-market-api-for-developers - -seo_title: "Which prediction market data API is the freshest in 2026?" -seo_description: "Polymarket's own CLOB gateway is the canonical T0 (fastest by definition). Among third-party providers, Codex leads freshness delta live, measured by OpenChainBench on the top 20 Polymarket and Kalshi markets by volume." -status: live diff --git a/benchmarks/abstract-rpc.yml b/benchmarks/abstract-rpc.yml new file mode 100644 index 00000000..d07924cd --- /dev/null +++ b/benchmarks/abstract-rpc.yml @@ -0,0 +1,136 @@ +# OpenChainBench. Bench № 125 + +slug: abstract-rpc +number: "125" +title: Fastest free Abstract RPC, live no-key EVM endpoint latency +seo_title: "Fastest free Abstract RPC 2026" +seo_description: "{{best_name}} leads free Abstract RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 3 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public Abstract EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Abstract is a ZK Stack validium L2 (chain 2741) built by the Abstract Foundation, optimised for consumer applications and NFTs, ETH gas token, ~2 s block cadence, ZK proof settlement onto Ethereum. The 2026-08-02 audit confirms 3 clean keyless providers: the Abstract Foundation official endpoint (api.mainnet.abs.xyz), dRPC (abstract.drpc.org), and Thirdweb (abstract.rpc.thirdweb.com). Excluded: Ankr (no abstract route on public tier), Alchemy (key required), Infura (key required), QuickNode (key required). + +abstract: | + Per-chain member of the RPC latency cluster, extended to Abstract. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + Abstract endpoint that sustains continuous probing, 3 providers + at launch, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with an Abstract-scaled staleness gap (20 blocks + at Abstract's ~2 s block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + Abstract-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"abstract\"`. Provider coverage at launch: 3 no-key endpoints." + +findings: + - "{{best_name}} currently leads free Abstract RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Abstract RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which Abstract RPCs work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: the Abstract Foundation official endpoint (api.mainnet.abs.xyz), dRPC (abstract.drpc.org), and Thirdweb (abstract.rpc.thirdweb.com). Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest Abstract RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is Abstract RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="abstract"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: abstract-official + name: Abstract Foundation + tag: Abstract Foundation official public RPC + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Abstract Foundation's no-key abstract RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="abstract-official", chain="abstract"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="abstract-official", chain="abstract"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="abstract-official", chain="abstract"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="abstract-official", chain="abstract"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="abstract-official", chain="abstract"}) / sum(ocb:rpc_call:rate_24h{provider="abstract-official", chain="abstract"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="abstract-official", chain="abstract"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="abstract-official", chain="abstract"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="abstract-official", chain="abstract", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="abstract-official", chain="abstract", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="abstract-official", chain="abstract", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="abstract-official", chain="abstract", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="abstract-official", chain="abstract", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="abstract-official", chain="abstract", region="sgp"}[1h]) + + - slug: drpc + name: dRPC + tag: dRPC public gateway routing to Abstract node pool + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to dRPC's no-key abstract RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="abstract"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="drpc", chain="abstract"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="drpc", chain="abstract"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="drpc", chain="abstract"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="drpc", chain="abstract"}) / sum(ocb:rpc_call:rate_24h{provider="drpc", chain="abstract"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="drpc", chain="abstract"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="abstract"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="abstract", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="abstract", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="abstract", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="abstract", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="abstract", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="abstract", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for Abstract + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key abstract RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="abstract"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="abstract"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="abstract"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="abstract"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="abstract"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="abstract"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="abstract"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="abstract"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="abstract", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="abstract", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="abstract", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="abstract", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="abstract", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="abstract", region="sgp"}[1h]) diff --git a/benchmarks/apechain-rpc.yml b/benchmarks/apechain-rpc.yml new file mode 100644 index 00000000..c4e09ba4 --- /dev/null +++ b/benchmarks/apechain-rpc.yml @@ -0,0 +1,136 @@ +# OpenChainBench. Bench № 126 + +slug: apechain-rpc +number: "126" +title: Fastest free ApeChain RPC, live no-key EVM endpoint latency +seo_title: "Fastest free ApeChain RPC 2026" +seo_description: "{{best_name}} leads free ApeChain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 3 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public ApeChain EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + ApeChain is an Arbitrum Orbit L3 (chain 33139) operated by Yuga Labs / ApeDAO, dedicated to the APE ecosystem, APE gas token, ~250 ms block cadence, settlement onto Arbitrum One. The 2026-08-02 audit confirms 3 clean keyless providers: the ApeChain official endpoint (rpc.apechain.com/http), dRPC (apechain.drpc.org), and Thirdweb (apechain.rpc.thirdweb.com). Excluded: Alchemy (key required), QuickNode (key required), Infura (no apechain route). + +abstract: | + Per-chain member of the RPC latency cluster, extended to ApeChain. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + ApeChain endpoint that sustains continuous probing, 3 providers + at launch, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with an ApeChain-scaled staleness gap (20 blocks + at ApeChain's ~250 ms block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + ApeChain-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"apechain\"`. Provider coverage at launch: 3 no-key endpoints." + +findings: + - "{{best_name}} currently leads free ApeChain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free ApeChain RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which ApeChain RPCs work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: the ApeChain official endpoint (rpc.apechain.com/http), dRPC (apechain.drpc.org), and Thirdweb (apechain.rpc.thirdweb.com). Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest ApeChain RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is ApeChain RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="apechain"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: apechain-official + name: ApeChain + tag: ApeChain official public RPC + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to ApeChain's no-key apechain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="apechain-official", chain="apechain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="apechain-official", chain="apechain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="apechain-official", chain="apechain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="apechain-official", chain="apechain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="apechain-official", chain="apechain"}) / sum(ocb:rpc_call:rate_24h{provider="apechain-official", chain="apechain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="apechain-official", chain="apechain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="apechain-official", chain="apechain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="apechain-official", chain="apechain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="apechain-official", chain="apechain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="apechain-official", chain="apechain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="apechain-official", chain="apechain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="apechain-official", chain="apechain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="apechain-official", chain="apechain", region="sgp"}[1h]) + + - slug: drpc + name: dRPC + tag: dRPC public gateway routing to ApeChain node pool + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to dRPC's no-key apechain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="apechain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="drpc", chain="apechain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="drpc", chain="apechain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="drpc", chain="apechain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="drpc", chain="apechain"}) / sum(ocb:rpc_call:rate_24h{provider="drpc", chain="apechain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="drpc", chain="apechain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="apechain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="apechain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="apechain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="apechain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="apechain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="apechain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="apechain", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for ApeChain + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key apechain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="apechain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="apechain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="apechain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="apechain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="apechain"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="apechain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="apechain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="apechain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="apechain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="apechain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="apechain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="apechain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="apechain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="apechain", region="sgp"}[1h]) diff --git a/benchmarks/cyber-rpc.yml b/benchmarks/cyber-rpc.yml new file mode 100644 index 00000000..09aa9296 --- /dev/null +++ b/benchmarks/cyber-rpc.yml @@ -0,0 +1,136 @@ +# OpenChainBench. Bench № 129 + +slug: cyber-rpc +number: "129" +title: Fastest free Cyber RPC, live no-key EVM endpoint latency +seo_title: "Fastest free Cyber Network RPC 2026" +seo_description: "{{best_name}} leads free Cyber Network RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 3 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public Cyber Network EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Cyber Network is an OP Stack L2 (chain 7560) operated by Cyber, a social-layer chain for SocialFi and Web3 social graphs, ETH gas token, ~2 s block cadence, blob calldata settlement on Ethereum. The 2026-08-02 audit confirms 3 clean keyless providers: Cyber official (rpc.cyber.co), AltLayer (cyber.alt.technology), and Thirdweb (7560.rpc.thirdweb.com). Excluded: dRPC (no cyber route), Blast API (no listing), Alchemy (key required). + +abstract: | + Per-chain member of the RPC latency cluster, extended to Cyber Network. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + Cyber Network endpoint that sustains continuous probing, 3 providers + at launch, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with a Cyber-scaled staleness gap (20 blocks + at Cyber's ~2 s block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + Cyber-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"cyber\"`. Provider coverage at launch: 3 no-key endpoints." + +findings: + - "{{best_name}} currently leads free Cyber Network RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Cyber Network RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which Cyber Network RPCs work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: Cyber official (rpc.cyber.co), AltLayer (cyber.alt.technology), and Thirdweb (7560.rpc.thirdweb.com, chain-ID URL). Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest Cyber Network RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is Cyber Network RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="cyber"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: cyber-official + name: Cyber Network + tag: Cyber Network official public RPC + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Cyber Network's no-key cyber RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="cyber-official", chain="cyber"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="cyber-official", chain="cyber"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="cyber-official", chain="cyber"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="cyber-official", chain="cyber"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="cyber-official", chain="cyber"}) / sum(ocb:rpc_call:rate_24h{provider="cyber-official", chain="cyber"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="cyber-official", chain="cyber"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="cyber-official", chain="cyber"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="cyber-official", chain="cyber", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="cyber-official", chain="cyber", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="cyber-official", chain="cyber", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="cyber-official", chain="cyber", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="cyber-official", chain="cyber", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="cyber-official", chain="cyber", region="sgp"}[1h]) + + - slug: altlayer + name: AltLayer + tag: AltLayer RPC node for Cyber Network + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to AltLayer's no-key cyber RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="altlayer", chain="cyber"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="altlayer", chain="cyber"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="altlayer", chain="cyber"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="altlayer", chain="cyber"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="altlayer", chain="cyber"}) / sum(ocb:rpc_call:rate_24h{provider="altlayer", chain="cyber"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="altlayer", chain="cyber"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="altlayer", chain="cyber"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="altlayer", chain="cyber", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="altlayer", chain="cyber", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="altlayer", chain="cyber", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="altlayer", chain="cyber", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="altlayer", chain="cyber", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="altlayer", chain="cyber", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for Cyber Network + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key cyber RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="cyber"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="cyber"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="cyber"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="cyber"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="cyber"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="cyber"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="cyber"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="cyber"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="cyber", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="cyber", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="cyber", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="cyber", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="cyber", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="cyber", region="sgp"}[1h]) diff --git a/benchmarks/dex-network-coverage.yml b/benchmarks/dex-network-coverage.yml index 29b361bd..19852251 100644 --- a/benchmarks/dex-network-coverage.yml +++ b/benchmarks/dex-network-coverage.yml @@ -4,7 +4,7 @@ slug: dex-network-coverage number: "090" title: DEX indexer with most blockchains, live coverage ranking seo_title: "DEX indexer most chains 2026" -seo_description: "Which DEX indexer supports the most blockchains? Live coverage ranking across GeckoTerminal, Codex, DefiLlama, DexPaprika and Sim by Dune." +seo_description: "Which DEX indexer supports the most blockchains? Live coverage ranking across GeckoTerminal, Codex, DexPaprika and Sim by Dune." subtitle: Number of blockchains where each major DEX indexer actively indexes pools + swap volumes, audited every six hours against each provider's public networks endpoint. category: Aggregators status: live @@ -20,29 +20,28 @@ seo_intro: | can this API return pool addresses, live swap events, OHLCV candles and trending pools. Providers on this leaderboard run their own DEX pool indexer as a product (GeckoTerminal `/networks`, Codex - `getNetworks`, DefiLlama `/chains`, DexPaprika `/networks`, Sim by - Dune `/evm/supported-chains`). Asset-registry coverage (chains where - an API knows tokens for contract-address lookup) is a different - question measured on the sister bench asset-registry-coverage. + `getNetworks`, DexPaprika `/networks`, Sim by Dune + `/evm/supported-chains`). Asset-registry coverage (chains where an + API knows tokens for contract-address lookup) is a different question + measured on the sister bench asset-registry-coverage. abstract: | We benchmark how many networks each major DEX indexer publishes as actively indexed for pools + swap volumes. The harness fetches the - official listing every six hours from five providers (GeckoTerminal, - Codex, DefiLlama, DexPaprika, Sim by Dune), deduplicates by chain id - or platform slug and counts. Mainnet only. Asset-registry coverage - (chains where the provider knows tokens for contract-address lookups) - is measured on bench asset-registry-coverage because it answers a - different question, an API can list tokens on 300 chains through a - market-data pipeline while running DEX pool indexing on only a - dozen. Both leaderboards live under Aggregators so a reader can - compare a provider on the axis that matches their product. + official listing every six hours from four providers (GeckoTerminal, + Codex, DexPaprika, Sim by Dune), deduplicates by chain id or platform + slug and counts. Mainnet only. Asset-registry coverage (chains where + the provider knows tokens for contract-address lookups) is measured + on bench asset-registry-coverage because it answers a different + question, an API can list tokens on 300 chains through a market-data + pipeline while running DEX pool indexing on only a dozen. Both + leaderboards live under Aggregators so a reader can compare a + provider on the axis that matches their product. methodology: - "Source: each provider's public DEX-indexer networks endpoint." - "GeckoTerminal: GET /api/v2/networks (paginated, no auth). Chains where GeckoTerminal actively indexes DEX pools + trades." - "Codex: GraphQL `getNetworks` query at https://graph.codex.io/graphql with an official API key. Chains covered by Defined.fi's DEX data pipeline." - - "DefiLlama: GET https://api.llama.fi/chains (no auth). Chains tracked by DefiLlama's TVL crawler, every chain with at least one indexed DeFi protocol, DEX chains are a subset." - "DexPaprika: GET https://api.dexpaprika.com/networks (no auth). CoinPaprika's dedicated DEX tracker product, separate from the market-data API." - "Sim by Dune: GET https://api.sim.dune.com/v1/evm/supported-chains (no auth). EVM only, mainnets filtered via the `mainnet` tag." - "Cadence: full refresh every 6 hours." @@ -53,7 +52,6 @@ findings: - "{{best_name}} currently leads at {{best_p50}} chains across {{count}} measured providers. The count is read live from each provider's DEX-networks endpoint, so the number reflects where each API actively indexes DEX pools + swap volumes today, not a marketing claim." - "{{name:geckoterminal}} sits at {{p50:geckoterminal}} chains via `/api/v2/networks`. GeckoTerminal indexes wherever CoinGecko's DEX pipeline reaches, so the count tracks the CoinGecko DEX-rollout roadmap rather than a standalone listing decision." - "{{name:codex}} clocks {{p50:codex}} chains via `getNetworks`. Defined.fi historically prioritises depth on EVM + Solana DEX coverage rather than breadth across niche chains, which surfaces here as a tighter count with stronger per-chain DEX quality than a raw breadth ranking implies." - - "{{name:defillama}} publishes {{p50:defillama}} chains via `/chains`. Broader than DEX-only (includes lending, LSTs, restaking, bridges) because the endpoint tracks every chain with at least one indexed DeFi protocol, DEX chains are a subset. The count is the canonical open reference for chains with meaningful onchain financial activity." - "DEX-indexer breadth is one axis of a data API. Asset-registry breadth (bench asset-registry-coverage) and per-chain head lag (bench aggregator-head-lag) measure the other axes a serious DEX-integration decision needs." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/network-coverage @@ -67,8 +65,6 @@ faq: a: "{{best_name}} currently leads at {{best_p50}} chains across {{count}} measured providers. The count is read every six hours directly from each provider's own DEX-networks endpoint and deduplicated by chain id, so the leaderboard reflects where each API actively indexes DEX pools + swap volumes today, not a claim from a marketing page." - q: "Why is this bench separate from asset-registry-coverage?" a: "DEX-pool coverage and asset-registry coverage are two different products, not a single metric. GeckoTerminal indexes DEX pools on ~265 chains via `/networks`; CoinPaprika lists 300+ chains in `/v1/contracts` because it indexes token metadata everywhere. Comparing them on one leaderboard makes the number meaningless, a market-data API always wins on chain count while a DEX indexer always wins on pool-level depth. This bench answers the DEX-indexing question. The sister bench asset-registry-coverage answers the token-lookup question." - - q: "Does DefiLlama really index DEX pools on 400+ chains?" - a: "DefiLlama's `/chains` endpoint tracks every chain with at least one indexed DeFi protocol (DEXes, lending, LSTs, restaking, bridges, oracles), so the count is broader than DEX-only. DEX chains are a subset. DefiLlama sits on the leaderboard because the same crawler backs their DEX-volumes aggregator, and the endpoint is the canonical open reference for `chains with meaningful onchain financial activity`. Read alongside the more DEX-specific counts from GeckoTerminal, Codex and DexPaprika for the DEX-only view." - q: "Why isn't CoinGecko / Mobula / CoinPaprika on this leaderboard?" a: "Those providers are measured on the sister bench asset-registry-coverage because their product is chain-scoped token metadata + market data, not DEX pool indexing. CoinGecko powers GeckoTerminal on the DEX side, but the token-registry number and the DEX-indexed number are measured separately because they answer different questions. Mobula and CoinPaprika are market-data APIs, not DEX pool indexers, DexPaprika is CoinPaprika's dedicated DEX product and is on this leaderboard." - q: "How often is the chain count refreshed?" @@ -77,8 +73,7 @@ faq: a: "Any chain a provider lists as a production network for DEX indexing, identified by a unique chain id or slug. Ethereum, Solana, Base, Arbitrum, BNB, Avalanche and so on each count once regardless of how the provider labels them internally. Testnet listings are filtered server-side where the provider exposes a testnet flag. Sim by Dune is EVM only by construction of the `/v1/evm/supported-chains` endpoint." # Real metrics exposed by the network-coverage harness: -# networks_supported_total{provider="geckoterminal"|"codex"| -# "defillama"|"dexpaprika"|"dune"} +# networks_supported_total{provider="geckoterminal"|"codex"|"dexpaprika"|"dune"} # -> gauge, the unique-chain count from the latest successful refresh. providers: @@ -108,19 +103,6 @@ providers: sample_size: networks_supported_total{provider="codex"} series: networks_supported_total{provider="codex"} - - slug: defillama - name: DefiLlama - tag: TVL + DEX-volumes aggregator - formula: "Count of chains returned by DefiLlama's public `/chains` endpoint (chains with at least one indexed DeFi protocol; DEX chains are a subset), refreshed every 6 hours." - queries: - p50: networks_supported_total{provider="defillama"} - p90: networks_supported_total{provider="defillama"} - p99: networks_supported_total{provider="defillama"} - mean: networks_supported_total{provider="defillama"} - success: clamp_max(networks_supported_total{provider="defillama"} > bool 0, 1) - sample_size: networks_supported_total{provider="defillama"} - series: networks_supported_total{provider="defillama"} - - slug: dexpaprika name: DexPaprika tag: CoinPaprika's DEX product diff --git a/benchmarks/kava-rpc.yml b/benchmarks/kava-rpc.yml new file mode 100644 index 00000000..6fa77742 --- /dev/null +++ b/benchmarks/kava-rpc.yml @@ -0,0 +1,182 @@ +# OpenChainBench. Bench № 121 + +slug: kava-rpc +number: "121" +title: Fastest free Kava RPC, live no-key EVM endpoint latency +seo_title: "Fastest free Kava RPC 2026" +seo_description: "{{best_name}} leads free Kava RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 5 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public Kava EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Kava is a Cosmos SDK Layer 1 (chain 2222) with a native EVM execution layer, ~5 s block time under Tendermint BFT consensus, KAVA gas token on the EVM side, and IBC connectivity to the wider Cosmos ecosystem. The 2026-08-03 audit confirms 5 clean keyless providers: the Kava Labs official endpoint (evm.kava.io), dRPC (kava.drpc.org), Thirdweb (kava.rpc.thirdweb.com), PublicNode (kava-evm-rpc.publicnode.com), and Ankr (rpc.ankr.com/kava_evm). Excluded: Tenderly (no kava route), Blast API (no listing), OnFinality (rate-limited without a key), 1RPC (unknown network). + +abstract: | + Per-chain member of the RPC latency cluster, extended to Kava EVM. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + Kava EVM endpoint that sustains continuous probing, 5 providers + measured, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with a Kava-scaled staleness gap (20 blocks + at Kava's ~5 s block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + Kava-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"kava\"`. Provider coverage: 5 no-key endpoints." + +findings: + - "{{best_name}} currently leads free Kava RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 5 measured providers." + +faq: + - q: "What is the fastest free Kava RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which Kava RPCs work without an API key?" + a: "5 endpoints sustain continuous keyless probing: the Kava Labs official endpoint (evm.kava.io), dRPC (kava.drpc.org), Thirdweb (kava.rpc.thirdweb.com), PublicNode (kava-evm-rpc.publicnode.com), and Ankr (rpc.ankr.com/kava_evm). Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest Kava RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is Kava RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="kava"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: kava-official + name: Kava Labs + tag: Kava Labs official public EVM RPC + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Kava Labs's no-key kava EVM RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="kava-official", chain="kava"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="kava-official", chain="kava"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="kava-official", chain="kava"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="kava-official", chain="kava"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="kava-official", chain="kava"}) / sum(ocb:rpc_call:rate_24h{provider="kava-official", chain="kava"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="kava-official", chain="kava"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="kava-official", chain="kava"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="kava-official", chain="kava", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="kava-official", chain="kava", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="kava-official", chain="kava", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="kava-official", chain="kava", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="kava-official", chain="kava", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="kava-official", chain="kava", region="sgp"}[1h]) + + - slug: drpc + name: dRPC + tag: dRPC public gateway routing to Kava node pool + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to dRPC's no-key kava RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="kava"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="drpc", chain="kava"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="drpc", chain="kava"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="drpc", chain="kava"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="drpc", chain="kava"}) / sum(ocb:rpc_call:rate_24h{provider="drpc", chain="kava"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="drpc", chain="kava"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="kava"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="kava", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="kava", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="kava", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="kava", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="kava", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="kava", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for Kava EVM + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key kava RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="kava"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="kava"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="kava"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="kava"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="kava"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="kava"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="kava"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="kava"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="kava", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="kava", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="kava", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="kava", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="kava", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="kava", region="sgp"}[1h]) + + - slug: publicnode + name: PublicNode + tag: PublicNode free public RPC for Kava EVM + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to PublicNode's no-key kava RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="publicnode", chain="kava"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="publicnode", chain="kava"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="publicnode", chain="kava"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="publicnode", chain="kava"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="publicnode", chain="kava"}) / sum(ocb:rpc_call:rate_24h{provider="publicnode", chain="kava"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="publicnode", chain="kava"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="publicnode", chain="kava"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="publicnode", chain="kava", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="publicnode", chain="kava", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="publicnode", chain="kava", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="publicnode", chain="kava", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="publicnode", chain="kava", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="publicnode", chain="kava", region="sgp"}[1h]) + + - slug: ankr + name: Ankr + tag: Ankr public RPC gateway for Kava EVM + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Ankr's no-key kava RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="kava"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="ankr", chain="kava"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="ankr", chain="kava"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="ankr", chain="kava"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="ankr", chain="kava"}) / sum(ocb:rpc_call:rate_24h{provider="ankr", chain="kava"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="ankr", chain="kava"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="kava"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="kava", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="kava", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="kava", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="kava", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="kava", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="kava", region="sgp"}[1h]) diff --git a/benchmarks/lisk-rpc.yml b/benchmarks/lisk-rpc.yml new file mode 100644 index 00000000..8ed450ab --- /dev/null +++ b/benchmarks/lisk-rpc.yml @@ -0,0 +1,136 @@ +# OpenChainBench. Bench № 127 + +slug: lisk-rpc +number: "127" +title: Fastest free Lisk RPC, live no-key EVM endpoint latency +seo_title: "Fastest free Lisk RPC 2026" +seo_description: "{{best_name}} leads free Lisk RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 3 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public Lisk EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Lisk is an OP Stack L2 (chain 1135) operated by Onchain Foundation (formerly Lisk Foundation), focused on emerging-market Web3 adoption, ETH gas token, ~2 s block cadence, blob calldata settlement on Ethereum. The 2026-08-02 audit confirms 3 clean keyless providers: the Lisk official endpoint (rpc.api.lisk.com), dRPC (lisk.drpc.org), and Thirdweb (lisk.rpc.thirdweb.com). Excluded: Blast API (no lisk route), Alchemy (key required), Infura (no lisk route), QuickNode (key required). + +abstract: | + Per-chain member of the RPC latency cluster, extended to Lisk. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + Lisk endpoint that sustains continuous probing, 3 providers + at launch, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with a Lisk-scaled staleness gap (20 blocks + at Lisk's ~2 s block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + Lisk-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"lisk\"`. Provider coverage at launch: 3 no-key endpoints." + +findings: + - "{{best_name}} currently leads free Lisk RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Lisk RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which Lisk RPCs work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: the Lisk official endpoint (rpc.api.lisk.com), dRPC (lisk.drpc.org), and Thirdweb (lisk.rpc.thirdweb.com). Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest Lisk RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is Lisk RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="lisk"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: lisk-official + name: Lisk + tag: Lisk official public RPC + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Lisk's no-key lisk RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="lisk-official", chain="lisk"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="lisk-official", chain="lisk"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="lisk-official", chain="lisk"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="lisk-official", chain="lisk"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="lisk-official", chain="lisk"}) / sum(ocb:rpc_call:rate_24h{provider="lisk-official", chain="lisk"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="lisk-official", chain="lisk"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="lisk-official", chain="lisk"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="lisk-official", chain="lisk", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="lisk-official", chain="lisk", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="lisk-official", chain="lisk", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="lisk-official", chain="lisk", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="lisk-official", chain="lisk", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="lisk-official", chain="lisk", region="sgp"}[1h]) + + - slug: drpc + name: dRPC + tag: dRPC public gateway routing to Lisk node pool + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to dRPC's no-key lisk RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="lisk"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="drpc", chain="lisk"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="drpc", chain="lisk"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="drpc", chain="lisk"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="drpc", chain="lisk"}) / sum(ocb:rpc_call:rate_24h{provider="drpc", chain="lisk"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="drpc", chain="lisk"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="lisk"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="lisk", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="lisk", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="lisk", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="lisk", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="lisk", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="lisk", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for Lisk + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key lisk RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="lisk"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="lisk"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="lisk"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="lisk"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="lisk"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="lisk"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="lisk"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="lisk"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="lisk", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="lisk", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="lisk", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="lisk", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="lisk", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="lisk", region="sgp"}[1h]) diff --git a/benchmarks/perp-active-markets.yml b/benchmarks/perp-active-markets.yml new file mode 100644 index 00000000..f3a93b90 --- /dev/null +++ b/benchmarks/perp-active-markets.yml @@ -0,0 +1,268 @@ +# OpenChainBench. Bench 116 + +slug: perp-active-markets +number: "116" +title: Most tradable markets on a perp DEX, live count across 14 venues +seo_title: "Perp DEX market count 2026" +seo_description: "Live count of active perpetual futures markets across 14 DEX venues: Aster, gains.trade, edgeX, Extended, Aevo, Lighter, Hyperliquid, GRVT, dYdX, Ostium, Pacifica, Paradex, Vertex, Polymarket. Refreshed every 5 minutes." +subtitle: Live count of active perpetual futures markets per venue, polled every 5 minutes from each venue's public API. Higher means more tradable instruments across crypto, forex, stocks, indices and commodities. +category: Trading +status: live +metric: Active markets +unit: count +higher_is_better: true + +seo_intro: | + This benchmark ranks 14 major onchain perp venues by total active markets. + Market count is the fastest proxy for breadth: a venue with 364 pairs gives + traders access to crypto, forex (EUR/USD, GBP/USD), tokenized stocks, commodities + and stock indices alongside BTC and ETH. We poll each venue's public API every + 5 minutes and publish one gauge per venue. gains.trade lists 364 pairs, the + widest catalog among EVM perp DEXes, vs Hyperliquid at 177 and dYdX at 114. + +abstract: | + The bench polls 14 perp DEX venues every 5 minutes and publishes a live count + of active markets per venue. Sources: native API for most venues (Hyperliquid + info metaAndAssetCtxs, Lighter orderBookDetails with market_type==perp filter, + dYdX v4 indexer perpetualMarkets, Paradex markets summary, edgeX native markets, + Aster fapi exchangeInfo PERPETUAL instruments, Vertex indexer, GRVT markets, + Extended public markets, Aevo markets, Pacifica Solana program state, Ostium + subgraph, Polymarket perps tickers). gains.trade uses the Mobula perp pairs + catalog (dex=gains, mainnet chains only, arbitrum-sepolia excluded), the same + source used by the perp-cohort-stats harness for all active_markets signals. + Higher is better. + +methodology: + - "Cadence: every 5 minutes per venue in parallel, 10 second timeout per request." + - "Hyperliquid: info metaAndAssetCtxs, count of non-delisted assets. Active perp markets only, crypto assets." + - "Lighter: orderBookDetails, count of markets where market_type==perp and status==active. Native API, mainnet only." + - "dYdX v4: indexer perpetualMarkets, count of non-deactivated markets." + - "Paradex: markets summary endpoint on Starknet L2, count of active perp markets." + - "edgeX: native markets endpoint, count of active perp markets on zkSync." + - "Aster: fapi exchangeInfo, count of instruments with contractType==PERPETUAL and status==TRADING on BNB Chain." + - "Vertex: public indexer, count of active perp products on Arbitrum." + - "GRVT: public markets endpoint, count of active instruments on ZK Stack." + - "Extended: public markets API, count of active perp markets on StarkEx." + - "Aevo: public markets endpoint, count of active perp instruments on OP Stack." + - "Pacifica: onchain reads on the Solana program state, count of active perp markets." + - "Ostium: Arbitrum subgraph, count of active pairs. Covers FX and commodities alongside crypto." + - "Polymarket: public perps tickers endpoint on Polygon, count of listed instruments." + - "gains.trade: Mobula perp pairs catalog (GET /api/2/perp/pairs, dex=gains, mainnet only). Covers crypto, forex, stocks, indices and commodities across Arbitrum, Base, Polygon and ApeChain." + - "Headline: avg_over_time of the live market count gauge over the last 24 hours. Listings change infrequently so the 24h average is effectively the current count." + - "Failures: a venue that errors keeps its last gauge value and its perp_venue_health gauge drops toward 0; the leaderboard tags it as stale." + +findings: + - "{{best_name}} leads the cohort with {{best_p50}} active markets across 14 measured perp DEX venues." + - "{{name:gains}} lists {{p50:gains}} active markets, spanning crypto, forex, stocks, indices and commodities. The widest multi-asset catalog among EVM perp DEXes." + - "{{name:aster}} lists {{p50:aster}} active markets. BNB Chain perps with a Binance-compatible API." + - "{{name:edgex}} lists {{p50:edgex}} active markets. zkSync-based offchain CLOB." + - "{{name:extended}} lists {{p50:extended}} active markets. StarkEx-based perps venue." + - "{{name:aevo}} lists {{p50:aevo}} active markets. Optimism L2 orderbook, options-focused history." + - "{{name:lighter}} lists {{p50:lighter}} active markets. Zero taker fee onchain orderbook on zkSync." + - "{{name:hyperliquid}} lists {{p50:hyperliquid}} active markets. HyperBFT L1 orderbook, crypto assets only." + - "{{name:grvt}} lists {{p50:grvt}} active markets. Hybrid CEX/DEX perps on ZK Stack." + - "{{name:dydx}} lists {{p50:dydx}} active markets. Cosmos appchain orderbook, one of the oldest decentralized perp venues." + - "{{name:ostium}} lists {{p50:ostium}} active markets. Synthetic perps on Arbitrum, FX and commodities alongside crypto." + - "{{name:pacifica}} lists {{p50:pacifica}} active markets. Solana-native onchain perps." + - "{{name:paradex}} lists {{p50:paradex}} active markets. Starknet L2 perps." + - "{{name:vertex}} lists {{p50:vertex}} active markets. Arbitrum orderbook plus AMM hybrid." + - "{{name:polymarket}} lists {{p50:polymarket}} active markets. Polygon perps, launched 2026-07." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/perp-cohort-stats + +prometheus: + window: 24h + expected_freshness_seconds: 1800 + +faq: + - q: "What does this benchmark measure?" + a: "The live count of active perpetual futures markets per venue. A market is a tradable instrument with a public price feed. Higher is better: more markets means more choice for traders." + - q: "Why does gains.trade use Mobula as a source instead of its native API?" + a: "The perp-cohort-stats harness uses the Mobula perp pairs catalog for gains because Mobula curates the full market taxonomy with asset-class tags. The count is mainnet-only (testnet chains excluded) and matches the inventory gains.trade exposes on its interface." + - q: "Is market count a good proxy for venue breadth?" + a: "It is the fastest proxy. A venue with 364 markets covering crypto, forex, stocks and commodities gives traders far more choice than one with 177 crypto-only markets. Depth per market is a separate signal; pair this bench with perp-open-interest and perp-fees for the full picture." + - q: "Why is Aster first if gains.trade covers more asset classes?" + a: "Aster lists more raw instruments (all USDT-margined pairs on BNB Chain including many micro-cap crypto assets). gains.trade lists fewer instruments but across a broader set of asset classes. The bench ranks by raw count; the findings text calls out gains as the widest multi-asset catalog among EVM DEXes." + +providers: + - slug: aster + name: Aster + tag: BNB Chain perps DEX, Binance-compatible API + formula: "Count of PERPETUAL instruments with status==TRADING from Aster fapi exchangeInfo. USDT-margined crypto pairs on BNB Chain." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="aster"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="aster"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="aster"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="aster"}[24h]) + success: avg_over_time(perp_venue_health{venue="aster"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="aster"}[24h]) + series: perp_venue_active_markets{venue="aster"} + + - slug: gains + name: gains.trade + tag: Synthetic perps on Arbitrum, Base, Polygon and ApeChain, multi-asset + formula: "Count of active mainnet perp pairs from Mobula's catalog (dex=gains, testnet excluded). Covers crypto, forex, stocks, indices and commodities." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="gains"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="gains"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="gains"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="gains"}[24h]) + success: avg_over_time(perp_venue_health{venue="gains"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="gains"}[24h]) + series: perp_venue_active_markets{venue="gains"} + + - slug: edgex + name: edgeX + tag: zkSync-based offchain CLOB + formula: "Count of active perp markets from edgeX native markets endpoint on zkSync." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="edgex"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="edgex"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="edgex"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="edgex"}[24h]) + success: avg_over_time(perp_venue_health{venue="edgex"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="edgex"}[24h]) + series: perp_venue_active_markets{venue="edgex"} + + - slug: extended + name: Extended + tag: StarkEx-based perps venue + formula: "Count of active perp markets from Extended's public markets API on StarkEx." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="extended"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="extended"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="extended"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="extended"}[24h]) + success: avg_over_time(perp_venue_health{venue="extended"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="extended"}[24h]) + series: perp_venue_active_markets{venue="extended"} + + - slug: aevo + name: Aevo + tag: Optimism L2 orderbook perps, options-focused history + formula: "Count of active perp instruments from Aevo's public markets endpoint on OP Stack." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="aevo"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="aevo"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="aevo"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="aevo"}[24h]) + success: avg_over_time(perp_venue_health{venue="aevo"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="aevo"}[24h]) + series: perp_venue_active_markets{venue="aevo"} + + - slug: lighter + name: Lighter + tag: zk-rollup, zero taker fee + formula: "Count of markets with market_type==perp and status==active from Lighter's orderBookDetails. Native API, mainnet only." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="lighter"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="lighter"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="lighter"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="lighter"}[24h]) + success: avg_over_time(perp_venue_health{venue="lighter"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="lighter"}[24h]) + series: perp_venue_active_markets{venue="lighter"} + + - slug: hyperliquid + name: Hyperliquid + tag: HyperBFT L1 perp DEX + formula: "Count of non-delisted assets from Hyperliquid info metaAndAssetCtxs. Active perp crypto markets only." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="hyperliquid"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="hyperliquid"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="hyperliquid"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="hyperliquid"}[24h]) + success: avg_over_time(perp_venue_health{venue="hyperliquid"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="hyperliquid"}[24h]) + series: perp_venue_active_markets{venue="hyperliquid"} + + - slug: grvt + name: GRVT + tag: Hybrid CEX/DEX perps on ZK Stack + formula: "Count of active instruments from GRVT public markets endpoint on ZK Stack." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="grvt"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="grvt"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="grvt"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="grvt"}[24h]) + success: avg_over_time(perp_venue_health{venue="grvt"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="grvt"}[24h]) + series: perp_venue_active_markets{venue="grvt"} + + - slug: dydx + name: dYdX v4 + tag: Cosmos appchain orderbook perps + formula: "Count of non-deactivated markets from the dYdX v4 indexer perpetualMarkets endpoint." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="dydx"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="dydx"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="dydx"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="dydx"}[24h]) + success: avg_over_time(perp_venue_health{venue="dydx"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="dydx"}[24h]) + series: perp_venue_active_markets{venue="dydx"} + + - slug: ostium + name: Ostium + tag: Synthetic perps on Arbitrum, FX and commodities alongside crypto + formula: "Count of active pairs from the Ostium subgraph on Arbitrum. Covers FX, commodities and crypto." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="ostium"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="ostium"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="ostium"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="ostium"}[24h]) + success: avg_over_time(perp_venue_health{venue="ostium"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="ostium"}[24h]) + series: perp_venue_active_markets{venue="ostium"} + + - slug: pacifica + name: Pacifica + tag: Solana-native onchain perps + formula: "Count of active perp markets from onchain reads on the Pacifica Solana program state." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="pacifica"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="pacifica"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="pacifica"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="pacifica"}[24h]) + success: avg_over_time(perp_venue_health{venue="pacifica"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="pacifica"}[24h]) + series: perp_venue_active_markets{venue="pacifica"} + + - slug: paradex + name: Paradex + tag: Starknet L2 perps + formula: "Count of active perp markets from the Paradex markets summary endpoint on Starknet L2." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="paradex"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="paradex"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="paradex"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="paradex"}[24h]) + success: avg_over_time(perp_venue_health{venue="paradex"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="paradex"}[24h]) + series: perp_venue_active_markets{venue="paradex"} + + - slug: vertex + name: Vertex + tag: Arbitrum orderbook plus AMM hybrid + formula: "Count of active perp products from the Vertex public indexer on Arbitrum." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="vertex"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="vertex"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="vertex"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="vertex"}[24h]) + success: avg_over_time(perp_venue_health{venue="vertex"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="vertex"}[24h]) + series: perp_venue_active_markets{venue="vertex"} + + - slug: polymarket + name: Polymarket + tag: Polygon perps, launched 2026-07 + formula: "Count of listed instruments from the Polymarket public perps tickers endpoint on Polygon." + queries: + p50: avg_over_time(perp_venue_active_markets{venue="polymarket"}[24h]) + p90: quantile_over_time(0.90, perp_venue_active_markets{venue="polymarket"}[24h]) + p99: quantile_over_time(0.99, perp_venue_active_markets{venue="polymarket"}[24h]) + mean: avg_over_time(perp_venue_active_markets{venue="polymarket"}[24h]) + success: avg_over_time(perp_venue_health{venue="polymarket"}[24h]) + sample_size: count_over_time(perp_venue_active_markets{venue="polymarket"}[24h]) + series: perp_venue_active_markets{venue="polymarket"} diff --git a/benchmarks/perp-cost-slope.yml b/benchmarks/perp-cost-slope.yml new file mode 100644 index 00000000..ee8f3802 --- /dev/null +++ b/benchmarks/perp-cost-slope.yml @@ -0,0 +1,236 @@ +# OpenChainBench. Bench 117 + +slug: perp-cost-slope +number: "117" +title: "Perp DEX cost invariance: all-in bps at $1M vs $1k, live slope ranked" +seo_title: "Perp DEX cost at $1M vs $1k 2026" +seo_description: "{{best_name}} leads perp cost invariance at {{best_p50}} cost slope (24h avg). Ratio of all-in bps at $1M vs $1k: oracle-priced venues score 1.00, orderbook venues above. Gains, GMX, Hyperliquid, dYdX ranked live." +subtitle: "Ratio of all-in opening cost at $1,000,000 vs $1,000 notional, per venue per asset. A venue with flat cost at every size scores 1.00. A deeper book scores closer to 1.00 than a thin one. Oracle-priced venues (no orderbook to walk) always score 1.00 by construction." +category: Trading +status: live +metric: "Cost slope ($1M / $1k)" +unit: x +higher_is_better: false + +seo_intro: | + Every perp DEX comparison article ranks venues by their published taker + fee rate. That number is size-independent by design: 4.5 bps on + Hyperliquid costs the same at $1,000 and $1,000,000 in taker fee alone. + What changes is the price impact from walking the orderbook. A $1M + market order eats deeper into the book than a $1k order, so the spread + component grows. Venues with no orderbook (oracle-priced: gains.trade, + GMX v2) are immune to this effect because there is no book to walk: the + protocol charges a flat percentage of notional regardless of size. On + those venues, a $1M trade costs exactly the same number of bps as a $1k + trade, giving a cost slope of 1.00. On an orderbook venue the slope + exceeds 1.00 by however much impact the extra notional adds. The wider + the gap above 1.00, the more the venue penalises large orders relative + to small ones. A venue whose visible book cannot absorb $1M at all shows + no slope because the $1M tier is skipped: that absence is a depth signal + stronger than any ratio. This benchmark derives its slope directly from + the existing perp-fees harness, which already measures all-in bps at + $1k, $10k, $100k and $1M per venue per asset every five minutes. + +abstract: | + The cost slope is the ratio of a venue's all-in opening cost at $1M + notional to its all-in cost at $1k notional. Both terms come from + perp_fees_all_in_bps_tier, the multi-tier companion series already + published by the perp-fees harness (bench 007). No new data collection + is required: the bench derives the ratio in PromQL at query time. A + ratio of 1.00 means the venue charges the same basis points per unit of + notional at $1M as at $1k, which is only possible for oracle-priced + venues where the fee is a flat percentage of position size with no + orderbook to walk. For orderbook venues the ratio rises above 1.00 + because deeper book levels carry progressively wider prices. The + companion fill-rate panel shows the fraction of measurement cycles in + which the $1M tier was successfully filled: venues that cannot reliably + absorb $1M show a fill rate below 1.00, which is a stronger signal than + any slope number. Data is refreshed every five minutes from the + perp-fees harness. The chain tabs scope the board to ETH, BTC or SOL + individually so the slope of a thin SOL book is not mixed with a deep + ETH book. + +methodology: + - "Cadence: derived in PromQL from perp_fees_all_in_bps_tier, refreshed every 5 minutes by the perp-fees harness (bench 007)." + - "Cost slope = avg_over_time(all_in_bps_tier{notional=1000000}[24h]) / ignoring(notional) avg_over_time(all_in_bps_tier{notional=1000}[24h]) per venue per asset. A ratio of 1.00 means flat cost across sizes." + - "Oracle-priced venues (gains.trade, GMX v2): the fee is a flat percentage of position size with no orderbook. The $1M all-in equals the $1k all-in in bps exactly, so slope = 1.00 always." + - "Orderbook venues (Hyperliquid, dYdX, Lighter, Extended, Aster, edgeX): the $1M tier walks deeper into the book than the $1k tier, so the spread component grows and slope > 1.00." + - "Missing $1M tier: when the visible book cannot absorb $1M the harness skips the tier and increments perp_fees_tier_skipped_total rather than extrapolating. The slope PromQL returns no data for that venue, shown as N/A in the leaderboard." + - "Fill-rate panel: count_over_time(tier{notional=1000000}[24h]) / ignoring(notional) count_over_time(tier{notional=1000}[24h]) per venue per asset. 1.00 = tier always filled; 0.50 = tier filled half the time; 0.00 = tier never filled." + - "Chain tabs: the board is scoped per asset (ETH, BTC, SOL) because book depth differs significantly across assets and cross-asset averaging obscures the signal." + - "Failures and stale data: inherited from the perp-fees harness. A venue that errors keeps its last gauge and its health drops to 0." + +per_chain_explainer: + - slug: ETH + h2: "ETH perp cost slope: size impact on the deepest pair" + body: | + ETH-PERP is the deepest pair on every venue in the cohort. On orderbook venues the $1M + tier is usually filled because ETH books carry several million USD of visible depth on the + ask side. The slope is therefore computable for most venues on ETH, and the spread between + the oracle-priced 1.00 and the orderbook venues reveals the raw cost of depth-limited execution. + Gains and GMX always read 1.00 because their protocol charges a flat open fee regardless of + notional; there is no book to walk for ETH on those venues. + - slug: BTC + h2: "BTC perp cost slope: tightest books, flattest slopes" + body: | + BTC carries the highest open interest on most venues and typically has the tightest spread + at any given notional. Orderbook venues therefore show their flattest slopes on BTC: the + $1M impact is minimised by depth. Oracle-priced venues (gains, gmx) remain at exactly 1.00 + regardless. + - slug: SOL + h2: "SOL perp cost slope: where thin books expose the gap" + body: | + SOL books are thinner than ETH or BTC on most venues. The $1M tier is frequently skipped + on smaller venues (shown as N/A), and on the venues that do fill it the slope is noticeably + higher than on ETH. This is the asset where the flat-cost advantage of oracle-priced venues + is most visible: gains and gmx score 1.00 while orderbook venues may show slopes of 1.50 + or higher. + +findings: + - "{{best_name}} leads the cost-slope leaderboard at {{best_p50}} (24h average). A slope of 1.00 means the venue charges the same basis points per unit of notional at $1M as at $1k." + - "{{name:gains}} and {{name:gmx}} are the only venues in the cohort that structurally score 1.00: they use oracle pricing with no orderbook, so the fee is a flat percentage of position size regardless of trade size." + - "{{name:hyperliquid}} sits at {{p50:hyperliquid}} cost slope (24h avg). The deepest onchain orderbook in the cohort keeps its impact low, but it still exceeds 1.00 at $1M notional on most assets." + - "Venues showing N/A in the slope column cannot reliably fill a $1M market order. The fill-rate panel shows what fraction of measurement cycles the $1M tier was successfully absorbed." + - "The cost slope is asset-specific: use the chain tabs to compare ETH (deepest books), BTC (tightest books) and SOL (thinnest books) separately." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/perp-fees + +prometheus: + window: 24h + expected_freshness_seconds: 1800 + freshness_metric: perp_fees_all_in_bps_tier + +faq: + - q: "What does cost slope of 1.00 mean?" + a: "A cost slope of 1.00 means the all-in basis points for a $1M trade are identical to a $1k trade on that venue. This is only possible for oracle-priced venues where the fee is a flat percentage of notional and there is no orderbook impact term." + - q: "Why are gains.trade and GMX always 1.00?" + a: "Both venues use oracle pricing with no orderbook. Their open fee (totalPositionSizeFeeP on Gains, positionFeeFactor on GMX) is a flat percentage of position size. Trading $1M costs the same bps as $1k by construction. This is the core design tradeoff: flat cost at any size, in exchange for using a price oracle rather than a live orderbook." + - q: "What does N/A in the slope column mean?" + a: "The venue's visible book could not absorb $1M of notional during the measurement period. The perp-fees harness skips a tier rather than extrapolate when the book depth is insufficient, so the $1M all-in gauge is absent. No slope can be computed. The fill-rate panel shows what fraction of cycles the $1M tier was successfully filled." + - q: "Is a 1.00 slope always better?" + a: "For a trader placing a $1M order: yes, a flat cost is better than a rising one. For a trader placing a $1k order: the slope is irrelevant because both the taker fee and the spread at small notional dominate regardless. The headline perp-fees bench (bench 007) covers the $1k case; this bench covers the size-scaling dimension." + - q: "Can an orderbook venue ever match 1.00?" + a: "In theory an infinitely deep book would have zero price impact at any size, giving a slope of exactly 1.00. In practice no onchain orderbook reaches that depth for sizes above $100k on SOL or above $1M on ETH or BTC. The fill rate panel shows how often a venue actually absorbs the full notional." + - q: "How does this differ from the perp-fees bench?" + a: "perp-fees (bench 007) ranks venues by absolute all-in cost at $1k notional. This bench ranks the same venues by how much their cost grows between $1k and $1M. A venue cheap at $1k can be expensive at $1M if its book is thin. The two benches together give a full picture: absolute cost at small size plus sensitivity to larger size." + +dimensions: + chain: + - { value: ETH, label: ETH } + - { value: BTC, label: BTC } + - { value: SOL, label: SOL } + +panel_main_label: "Cost slope ($1M / $1k)" +metric_panels: + - id: fill_rate_1m + label: "$1M fill rate" + metric: "avg(count_over_time(perp_fees_all_in_bps_tier{notional=\"1000000\"}[24h]) / ignoring(notional) count_over_time(perp_fees_all_in_bps_tier{notional=\"1000\"}[24h])) * 100" + label_key: venue + unit: pct + higher_is_better: true + description: "Fraction of 5-minute cycles in which the venue's visible book successfully absorbed $1M of buy notional for the selected asset. 100% = always filled; oracle-priced venues always fill. A low number means the book is too thin to reliably absorb large orders." + +providers: + - slug: gains + name: gains.trade + tag: "Synthetic perps on Base, oracle-priced, flat cost at any size" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Oracle-priced (flat % of notional, no orderbook): ratio always 1.00 by construction." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="gains"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="gains",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="gains",notional="1000000"} + + - slug: gmx + name: GMX v2 + tag: "Synthetics on Arbitrum, oracle-priced, flat cost at any size" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Oracle-priced (positionFeeFactor flat % of notional, no orderbook): ratio always 1.00." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="gmx"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="gmx",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="gmx",notional="1000000"} + + - slug: hyperliquid + name: Hyperliquid + tag: "HyperBFT L1 perp DEX, deepest onchain book" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: $1M tier walks deeper than $1k, raising the spread component above 1.00." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="hyperliquid"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="hyperliquid",notional="1000000"} + + - slug: lighter + name: Lighter + tag: "zk-rollup, zero taker fee, onchain orderbook" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: $1M tier walks 100 levels of the Lighter zk-rollup book." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="lighter"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="lighter",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="lighter",notional="1000000"} + + - slug: dydx + name: dYdX v4 + tag: "Cosmos appchain orderbook" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: full dYdX v4 indexer book walked at each tier." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="dydx"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="dydx",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="dydx",notional="1000000"} + + - slug: extended + name: Extended + tag: "Starknet perps, full public book" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: full public Starknet book walked at each tier." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="extended"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="extended",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="extended",notional="1000000"} + + - slug: aster + name: Aster + tag: "BNB Chain perp DEX, Binance-style REST book" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: BNB Chain CLOB, fapi depth endpoint walked per tier." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="aster"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="aster",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="aster",notional="1000000"} + + - slug: edgex + name: edgeX + tag: "Offchain CLOB perp DEX" + formula: "all_in_bps_tier{notional=1000000}[24h] / all_in_bps_tier{notional=1000}[24h]. Orderbook: offchain CLOB, public getDepth endpoint walked per tier." + queries: + p50: avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000"}[24h]) + p90: avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="100000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000"}[24h]) + p99: avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="10000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000"}[24h]) + mean: avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000000"}[24h]) / ignoring(notional) avg_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000"}[24h]) + success: avg_over_time(perp_fees_health{venue="edgex"}[24h]) + sample_size: count_over_time(perp_fees_all_in_bps_tier{venue="edgex",notional="1000000"}[24h]) + series: perp_fees_all_in_bps_tier{venue="edgex",notional="1000000"} diff --git a/benchmarks/perp-mark-price-lag.yml b/benchmarks/perp-mark-price-lag.yml new file mode 100644 index 00000000..63b848cb --- /dev/null +++ b/benchmarks/perp-mark-price-lag.yml @@ -0,0 +1,180 @@ +# OpenChainBench. Bench 118 + +slug: perp-mark-price-lag +number: "118" +title: "Perp DEX mark price fidelity: deviation from CEX spot reference, live" +seo_title: "Perp DEX mark price accuracy 2026" +seo_description: "{{best_name}} leads mark price fidelity at {{best_p50}} bps deviation from CEX spot (24h p50). Oracle-priced venues vs orderbook venues on ETH, BTC and SOL. gains.trade, GMX, Hyperliquid, dYdX, Lighter, Paradex ranked live." +subtitle: "Median absolute deviation in basis points between each venue's mark price and a Binance spot reference, measured every 60 seconds on ETH, BTC and SOL. Lower means the venue's mark price stays closer to the CEX spot consensus. Oracle-priced venues use a real-time price feed anchored to CEX spot; orderbook venues derive their mark from their own book." +category: Trading +status: live +metric: Mark price deviation +unit: bps +higher_is_better: false + +seo_intro: | + Every perpetual DEX publishes a mark price: the price used to compute + unrealised P&L, trigger liquidations and calculate funding. For a + trader, the mark price being far from the real market price is not a + minor inconvenience, it is a liquidation risk. A venue whose mark price + persistently sits 50 bps above the real price will liquidate a short at + a price that never existed on any exchange. This benchmark measures + how far each venue's live mark price sits from a Binance spot reference + on ETH, BTC and SOL, sampled every 60 seconds. Oracle-priced venues + (gains.trade, GMX v2) source their mark from a real-time price oracle + (Pyth or Chainlink) that itself tracks CEX spot continuously. Their + deviation from Binance spot is therefore structurally small: the oracle + interpolates from the same underlying CEX feed we use as reference. + Orderbook venues (Hyperliquid, dYdX, Lighter, Paradex) derive their + mark price from their own book, which can temporarily diverge from the + broader market during low-liquidity windows or fast-moving events. The + benchmark does not attempt to capture sub-second events such as + liquidation-triggering wicks, which require websocket resolution to + observe. It measures the persistent component: how far a venue's mark + price sits from CEX spot on a 60-second polling cadence. + +abstract: | + The harness polls each venue's published mark price every 60 seconds + alongside a Binance spot reference price (best bid/ask mid). Mark price + deviation is defined as the absolute value of (mark - reference) / + reference x 10000, in basis points. For oracle-priced venues + (gains.trade, GMX v2) the harness reads the Pyth price feed for the + same asset, which is the primary oracle both protocols weight heavily + in their mark price computation. Pyth updates sub-second from market + maker quotes that track CEX spot, so the deviation from Binance spot + is near zero structurally. For Hyperliquid the harness reads markPx + from the metaAndAssetCtxs endpoint. For dYdX v4 it reads indexPrice + from the perpetualMarkets indexer endpoint. For Lighter and Paradex + it reads the orderbook mid as an approximation of their mark price. + The headline metric is the 24h p50 of the deviation series, which + captures typical behaviour rather than isolated spikes. The p99 series + is exposed for those interested in tail risk. The signed deviation + (positive means the venue mark is above the reference) is published + separately as perp_mark_deviation_signed_bps. + +methodology: + - "Cadence: every 60 seconds in parallel across all venues. Reference price and venue mark prices are fetched in the same cycle, so the comparison is time-aligned." + - "Reference price: Binance REST /api/v3/ticker/bookTicker for the selected asset (ETHUSDT, BTCUSDT, SOLUSDT). The harness uses the mid of best bid and best ask as the reference." + - "gains.trade (Gains v8, oracle-priced): Pyth hermes API /v2/updates/price/latest for the ETH/USD, BTC/USD, SOL/USD feed IDs. Pyth is the primary real-time oracle Gains v8 weights in its mark price computation. Deviation from Binance spot is structurally near zero." + - "GMX v2 (oracle-priced): same Pyth hermes API endpoint. GMX v2 uses Chainlink as primary oracle; Pyth prices are an accepted proxy since both track CEX spot within 1 bps in normal conditions. Conservative labelling: formula tooltip notes the Pyth proxy." + - "Hyperliquid: POST /info {type:metaAndAssetCtxs} returns markPx per asset. This is the venue's own mark price used for P&L and liquidations, derived from their HyperBFT oracle." + - "dYdX v4: GET /v4/perpetualMarkets from the dYdX indexer returns indexPrice per market. indexPrice is the oracle-anchored price dYdX uses for margin and liquidation calculations." + - "Lighter: orderbook mid computed from GET /api/v1/orderBookOrders best bid and ask. Lighter does not publish a separate mark price endpoint; the orderbook mid is the closest available approximation." + - "Paradex: GET /markets endpoint returns markPrice per instrument on Starknet L2." + - "Deviation formula: abs(mark - reference) / reference x 10000, in basis points. Signed version (mark - reference) / reference x 10000 also published." + - "Failure handling: a venue that errors or times out leaves the previous gauge in place and increments perp_mark_fetch_errors_total. The health gauge drops to 0, flagging the row as stale in the UI." + - "Scope: this bench measures the persistent deviation visible at 60-second polling. Sub-second wick events that resolve within a single poll interval are not captured. The methodology page names this explicitly to avoid overstating what is measured." + +findings: + - "{{best_name}} leads mark price fidelity at {{best_p50}} bps median deviation from Binance spot (24h p50 across ETH, BTC and SOL)." + - "{{name:gains}} and {{name:gmx}} are the oracle-priced venues: their mark price is derived from Pyth, which itself aggregates from CEX market makers and tracks Binance spot within 1 bps in normal conditions. Their structural deviation is near zero." + - "{{name:hyperliquid}} sits at {{p50:hyperliquid}} bps median deviation. Hyperliquid's oracle is a volume-weighted median of external CEX prices, so it tracks spot closely but with a short latency window." + - "{{name:dydx}} sits at {{p50:dydx}} bps median deviation. dYdX uses an oracle-anchored index price that lags spot by at most a few seconds under normal conditions." + - "{{name:lighter}} and {{name:paradex}} derive their mark from their orderbook, which can diverge from spot during low-liquidity windows." + - "The p99 series (available in the Series tab) captures tail deviations. A high p99 relative to p50 signals occasional windows of elevated divergence." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/perp-mark-price-lag + +prometheus: + window: 24h + expected_freshness_seconds: 600 + freshness_metric: perp_mark_deviation_bps + +faq: + - q: "What is the mark price and why does its deviation matter?" + a: "The mark price is the price each venue uses to compute unrealised P&L, trigger liquidations and settle funding payments. A mark price that diverges significantly from the real market price creates liquidation risk: a long position can be margin-called at a loss even if the actual market price never touched the liquidation level." + - q: "Why do oracle-priced venues structurally win this bench?" + a: "Oracle-priced venues (gains.trade, GMX v2) derive their mark from an external price oracle such as Pyth or Chainlink. Both oracles aggregate from CEX market maker quotes that continuously track Binance spot, so the deviation is structurally near zero. Orderbook venues derive their mark from their own book, which can temporarily lag or diverge from the broader market." + - q: "What is the Pyth proxy used for gains.trade and GMX?" + a: "The harness reads the Pyth hermes REST API for the ETH/USD, BTC/USD and SOL/USD price IDs. This is used as the oracle-priced mark proxy for both gains and gmx. Gains v8 weights Pyth heavily; GMX v2 primarily uses Chainlink. The methodology tooltip on each row discloses this proxy. Chainlink ETH/USD on Arbitrum tracks Pyth ETH/USD within 1 bps in normal conditions." + - q: "Does this bench capture scam wicks?" + a: "No. Scam wicks that resolve within a single 60-second poll interval are not captured. This bench measures the persistent component of mark price divergence: how far a venue typically sits from CEX spot across a full day of 60-second samples. Sub-second wick analysis requires websocket resolution on both sides simultaneously." + - q: "What does the signed deviation metric show?" + a: "The signed deviation (positive = venue mark above reference, negative = below) is published as perp_mark_deviation_signed_bps. A persistent positive bias means the venue's mark is above CEX spot, which inflates longs' unrealised P&L while squeezing shorts. A persistent negative bias does the opposite." + - q: "Why is Lighter's mark price approximated from the orderbook mid?" + a: "Lighter does not expose a dedicated mark price endpoint in its public API. The orderbook best-bid-best-ask mid is the closest available approximation and is used consistently across all cycles." + +dimensions: + chain: + - { value: ETH, label: ETH } + - { value: BTC, label: BTC } + - { value: SOL, label: SOL } + +providers: + - slug: gains + name: gains.trade + tag: "Oracle-priced via Pyth on Base, structurally near-zero deviation" + formula: "24h p50 of abs(Pyth ETH/USD price - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. Pyth is the primary real-time oracle weighted by Gains v8. Binance spot mid is the reference." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="gains"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="gains"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="gains"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="gains"}[24h]) + success: avg_over_time(perp_mark_health{venue="gains"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="gains"}[24h]) + series: perp_mark_deviation_bps{venue="gains"} + + - slug: gmx + name: GMX v2 + tag: "Oracle-priced, Pyth proxy for Chainlink on Arbitrum" + formula: "24h p50 of abs(Pyth ETH/USD price - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. GMX v2 uses Chainlink as primary oracle; Pyth is used as an accepted proxy. Disclosed in methodology." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="gmx"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="gmx"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="gmx"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="gmx"}[24h]) + success: avg_over_time(perp_mark_health{venue="gmx"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="gmx"}[24h]) + series: perp_mark_deviation_bps{venue="gmx"} + + - slug: hyperliquid + name: Hyperliquid + tag: "HyperBFT L1, markPx from metaAndAssetCtxs" + formula: "24h p50 of abs(Hyperliquid markPx - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. markPx from POST /info {type:metaAndAssetCtxs}." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="hyperliquid"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="hyperliquid"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="hyperliquid"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="hyperliquid"}[24h]) + success: avg_over_time(perp_mark_health{venue="hyperliquid"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="hyperliquid"}[24h]) + series: perp_mark_deviation_bps{venue="hyperliquid"} + + - slug: dydx + name: dYdX v4 + tag: "Cosmos appchain, indexPrice from perpetualMarkets indexer" + formula: "24h p50 of abs(dYdX indexPrice - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. indexPrice from GET /v4/perpetualMarkets on the dYdX indexer." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="dydx"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="dydx"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="dydx"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="dydx"}[24h]) + success: avg_over_time(perp_mark_health{venue="dydx"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="dydx"}[24h]) + series: perp_mark_deviation_bps{venue="dydx"} + + - slug: lighter + name: Lighter + tag: "zk-rollup, orderbook mid used as mark price proxy" + formula: "24h p50 of abs(Lighter orderbook mid - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. Lighter does not expose a dedicated mark price endpoint; the orderbook mid is used as an approximation." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="lighter"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="lighter"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="lighter"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="lighter"}[24h]) + success: avg_over_time(perp_mark_health{venue="lighter"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="lighter"}[24h]) + series: perp_mark_deviation_bps{venue="lighter"} + + - slug: paradex + name: Paradex + tag: "Starknet appchain, markPrice from /markets" + formula: "24h p50 of abs(Paradex markPrice - Binance spot mid) / Binance spot mid x 10000, sampled every 60 seconds. markPrice from GET /markets on Starknet L2." + queries: + p50: quantile_over_time(0.50, perp_mark_deviation_bps{venue="paradex"}[24h]) + p90: quantile_over_time(0.90, perp_mark_deviation_bps{venue="paradex"}[24h]) + p99: quantile_over_time(0.99, perp_mark_deviation_bps{venue="paradex"}[24h]) + mean: avg_over_time(perp_mark_deviation_bps{venue="paradex"}[24h]) + success: avg_over_time(perp_mark_health{venue="paradex"}[24h]) + sample_size: count_over_time(perp_mark_deviation_bps{venue="paradex"}[24h]) + series: perp_mark_deviation_bps{venue="paradex"} diff --git a/benchmarks/perp-open-interest.yml b/benchmarks/perp-open-interest.yml deleted file mode 100644 index e93cb926..00000000 --- a/benchmarks/perp-open-interest.yml +++ /dev/null @@ -1,259 +0,0 @@ -# OpenChainBench. Bench № 042 - -slug: perp-open-interest -number: "042" -title: Perp DEX open interest, live USD notional ranked -seo_title: "Perp DEX open interest 2026" -seo_description: "Live perpetual futures open interest in USD across 13 major perp DEX venues: Hyperliquid, Aster, Lighter, GRVT, Extended, Ostium, Pacifica, Vertex, dYdX, Aevo, Paradex, gains.trade, Polymarket. Refreshed every 5 minutes." -subtitle: Live aggregate open interest in USD across 13 major decentralized perpetual futures venues. Higher means more notional sits open. Read live from each venue's public API. -category: Trading -status: live -metric: Open interest -unit: usd -higher_is_better: true - -seo_intro: | - This benchmark ranks the major onchain perp venues by aggregate open - interest, in USD. Open interest is the canonical depth-of-market - number for perpetual futures: it tells you how much notional sits - open right now, on every market, on a given venue. We poll each - venue's public API every 5 minutes, sum open interest across all - listed perps, and publish one gauge per venue. The cohort covers 13 - venues: Hyperliquid, Aster, Lighter, GRVT, Extended, Ostium, Pacifica, - Vertex, dYdX v4, Aevo, Paradex and gains.trade. Headline number is the 24h average - of the live OI gauge so a single noisy print does not move the - ranking. - -abstract: | - The bench polls 13 perp DEX venues every 5 minutes for live open - interest and exposes a USD-aggregated gauge per venue. Each venue's - public API is queried directly (Hyperliquid info metaAndAssetCtxs, - Aster fapi openInterest, Lighter orderBookDetails, GRVT public - markets, Extended API, Ostium subgraph, Pacifica onchain reads, - Vertex indexer, dYdX v4 indexer, Aevo public markets, Paradex - markets summary, gains.trade trading-variables per collateral across - Arbitrum/Base/Polygon/ApeChain) and the notional is summed in USD - across all listed perps. Each value carries a freshness timestamp and a health - gauge so a stale or errored venue does not poison the leaderboard. - 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." - - "Lighter: orderBookDetails per market, open interest summed across markets in USD." - - "GRVT: public markets endpoint, open interest per instrument summed in USD." - - "Extended: public markets API, open interest per instrument summed in USD." - - "Ostium: subgraph read for open interest across all listed pairs on Arbitrum." - - "Pacifica: onchain reads on the Solana program state for aggregate open interest." - - "Vertex: public indexer, open interest per product summed across all perps." - - "dYdX v4: indexer perpetualMarkets, openInterest per market summed in USD." - - "Aevo: public markets endpoint, open interest per instrument summed in USD." - - "Paradex: markets summary endpoint on Starknet L2, open interest summed across perps." - - "gains.trade: trading-variables endpoint per chain backend (Arbitrum, Base, Polygon, ApeChain), per-collateral pairOis summed then multiplied by collateralPriceUsd, summed across all chains." - - "Headline. avg_over_time of the live OI gauge over the last 24 hours, so a one-print spike does not move the ranking. The Series tab plots the raw gauge." - - "Failures. A venue that errors or times out keeps its last gauge value and its perp_venue_health gauge drops toward 0; the leaderboard tags it as stale." - -findings: - - "{{best_name}} currently leads the cohort at {{best_p50}} of open interest (24h average) across 13 measured perp DEX venues." - - "{{name:hyperliquid}} sits at {{p50:hyperliquid}} of open interest. HyperBFT orderbook is the deepest decentralized perp venue by a wide margin." - - "{{name:lighter}} clocks {{p50:lighter}} of open interest. Fully onchain orderbook on zkSync with zero taker fee." - - "{{name:aster}} sits at {{p50:aster}} of open interest. BNB Chain perps DEX with a Binance-compatible API surface." - - "{{name:grvt}} sits at {{p50:grvt}} of open interest. Hybrid CEX/DEX perps on ZK Stack." - - "{{name:extended}} sits at {{p50:extended}} of open interest. StarkEx-based perps venue." - - "{{name:pacifica}} sits at {{p50:pacifica}} of open interest. Solana-native onchain perps." - - "{{name:dydx}} sits at {{p50:dydx}} of open interest. Cosmos appchain orderbook, one of the oldest decentralized perp venues in the cohort." - - "{{name:ostium}} sits at {{p50:ostium}} of open interest. Synthetic perps on Arbitrum covering FX and commodities alongside crypto." - - "{{name:vertex}} sits at {{p50:vertex}} of open interest. Arbitrum orderbook plus AMM hybrid." - - "{{name:paradex}} sits at {{p50:paradex}} of open interest. Starknet L2 perps." - - "{{name:aevo}} sits at {{p50:aevo}} of open interest. Optimism L2 orderbook perps, options-focused history." - - "{{name:gains}} sits at {{p50:gains}} of open interest. Synthetic perps across Arbitrum, Base, Polygon and ApeChain." - -disclaimer: "gains.trade aggregates open interest across four chains (Arbitrum, Base, Polygon, ApeChain) into a single number, while most other venues operate on one chain. This inflates gains.trade's total relative to single-chain venues and makes a direct OI comparison unfair. The per-venue tags call this out; for a single-chain view of gains.trade, use the per-chain breakdown once available." - -source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/perp-cohort-stats - -prometheus: - window: 24h - expected_freshness_seconds: 1800 - -faq: - - q: "What does this benchmark measure?" - a: "The live aggregate open interest of 13 major perp DEX venues in USD. Headline is the 24h average of the live OI gauge so single prints do not skew the ranking." - - q: "How is open interest different from volume?" - a: "Volume counts every trade that crosses the book during a window. Open interest counts notional that currently sits open, regardless of when it was opened. A high-volume venue with high turnover can carry lower OI than a slower venue with sticky positions." - - q: "Which venues are in the cohort?" - a: "Hyperliquid, Aster, Lighter, GRVT, Extended, Ostium, Pacifica, Vertex, dYdX v4, Aevo, Paradex, gains.trade and Polymarket, the 13 decentralized perp venues whose public APIs expose open interest cleanly enough to compare without backfilling. Other venues join as their endpoints document the same series." - - q: "Is OI a good proxy for venue health?" - a: "It is one of the cleanest. A venue can spike volume with wash trading or incentive programs, but real OI is harder to fake because it ties up margin. Compared against perp-volume-share, persistent OI dominance signals real position-holding flow rather than turnover." - -providers: - - slug: hyperliquid - name: Hyperliquid - tag: HyperBFT L1 perp DEX - formula: "Open interest from Hyperliquid info metaAndAssetCtxs, summed across all assets and priced in USD using the venue's mark price; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="hyperliquid"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="hyperliquid"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="hyperliquid"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="hyperliquid"}[24h]) - success: avg_over_time(perp_venue_health{venue="hyperliquid"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="hyperliquid"}[24h]) - series: perp_venue_oi_usd{venue="hyperliquid"} - - - slug: polymarket - name: Polymarket - tag: Polygon perps by Polymarket, launched 2026-07, public info API - formula: "open_interest (base units) times mark_price from the Polymarket perps tickers endpoint, summed across all listed instruments in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="polymarket"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="polymarket"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="polymarket"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="polymarket"}[24h]) - success: avg_over_time(perp_venue_health{venue="polymarket"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="polymarket"}[24h]) - series: perp_venue_oi_usd{venue="polymarket"} - - - slug: aster - name: Aster - tag: BNB Chain perps DEX, Binance-compatible API - formula: "Open interest from Aster fapi openInterest per instrument, summed across all listed USDT perps; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="aster"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="aster"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="aster"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="aster"}[24h]) - success: avg_over_time(perp_venue_health{venue="aster"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="aster"}[24h]) - series: perp_venue_oi_usd{venue="aster"} - - - slug: lighter - name: Lighter - tag: zk-rollup, zero taker fee - formula: "Open interest from Lighter orderBookDetails per market, summed across markets in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="lighter"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="lighter"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="lighter"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="lighter"}[24h]) - success: avg_over_time(perp_venue_health{venue="lighter"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="lighter"}[24h]) - series: perp_venue_oi_usd{venue="lighter"} - - - slug: grvt - name: GRVT - tag: Hybrid CEX/DEX perps on ZK Stack - formula: "Open interest from GRVT public markets endpoint per instrument, summed in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="grvt"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="grvt"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="grvt"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="grvt"}[24h]) - success: avg_over_time(perp_venue_health{venue="grvt"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="grvt"}[24h]) - series: perp_venue_oi_usd{venue="grvt"} - - - slug: extended - name: Extended - tag: StarkEx-based perps venue - formula: "Open interest from Extended public markets API per instrument, summed in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="extended"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="extended"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="extended"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="extended"}[24h]) - success: avg_over_time(perp_venue_health{venue="extended"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="extended"}[24h]) - series: perp_venue_oi_usd{venue="extended"} - - - slug: ostium - name: Ostium - tag: Synthetic perps on Arbitrum, FX and commodities alongside crypto - formula: "Open interest from the Ostium subgraph across all listed pairs on Arbitrum, summed in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="ostium"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="ostium"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="ostium"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="ostium"}[24h]) - success: avg_over_time(perp_venue_health{venue="ostium"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="ostium"}[24h]) - series: perp_venue_oi_usd{venue="ostium"} - - - slug: pacifica - name: Pacifica - tag: Solana-native onchain perps - formula: "Open interest read onchain from the Pacifica Solana program state, summed across all listed perps; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="pacifica"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="pacifica"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="pacifica"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="pacifica"}[24h]) - success: avg_over_time(perp_venue_health{venue="pacifica"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="pacifica"}[24h]) - series: perp_venue_oi_usd{venue="pacifica"} - - - slug: vertex - name: Vertex - tag: Arbitrum orderbook plus AMM hybrid - formula: "Open interest from the Vertex public indexer per product, summed across all perps in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="vertex"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="vertex"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="vertex"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="vertex"}[24h]) - success: avg_over_time(perp_venue_health{venue="vertex"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="vertex"}[24h]) - series: perp_venue_oi_usd{venue="vertex"} - - - slug: dydx - name: dYdX v4 - tag: Cosmos appchain orderbook perps - formula: "Open interest from the dYdX v4 indexer perpetualMarkets endpoint, summed per market in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="dydx"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="dydx"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="dydx"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="dydx"}[24h]) - success: avg_over_time(perp_venue_health{venue="dydx"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="dydx"}[24h]) - series: perp_venue_oi_usd{venue="dydx"} - - - slug: aevo - name: Aevo - tag: Optimism L2 orderbook perps, options-focused history - formula: "Open interest from the Aevo public markets endpoint per instrument, summed in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="aevo"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="aevo"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="aevo"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="aevo"}[24h]) - success: avg_over_time(perp_venue_health{venue="aevo"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="aevo"}[24h]) - series: perp_venue_oi_usd{venue="aevo"} - - - slug: paradex - name: Paradex - tag: Starknet L2 perps - formula: "Open interest from the Paradex markets summary endpoint on Starknet L2, summed across perps in USD; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="paradex"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="paradex"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="paradex"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="paradex"}[24h]) - success: avg_over_time(perp_venue_health{venue="paradex"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="paradex"}[24h]) - series: perp_venue_oi_usd{venue="paradex"} - - - slug: gains - name: gains.trade - tag: Synthetic perps across Arbitrum + Base + Polygon + ApeChain (4-chain aggregate — not directly comparable to single-chain venues) - formula: "Open interest from the gains.trade trading-variables endpoint per chain backend: per-collateral pairOis long+short, normalized by precision x collateralPriceUsd, summed across 4 chains; headline is the 24h time average." - queries: - p50: avg_over_time(perp_venue_oi_usd{venue="gains"}[24h]) - p90: quantile_over_time(0.90, perp_venue_oi_usd{venue="gains"}[24h]) - p99: quantile_over_time(0.99, perp_venue_oi_usd{venue="gains"}[24h]) - mean: avg_over_time(perp_venue_oi_usd{venue="gains"}[24h]) - success: avg_over_time(perp_venue_health{venue="gains"}[24h]) - sample_size: count_over_time(perp_venue_oi_usd{venue="gains"}[24h]) - series: perp_venue_oi_usd{venue="gains"} diff --git a/benchmarks/perp-protocol-longevity.yml b/benchmarks/perp-protocol-longevity.yml new file mode 100644 index 00000000..efedea82 --- /dev/null +++ b/benchmarks/perp-protocol-longevity.yml @@ -0,0 +1,174 @@ +# OpenChainBench. Bench 119 + +slug: perp-protocol-longevity +number: "119" +title: "Perp DEX security track record: days without a recorded exploit, live" +seo_title: "Perp DEX security track record 2026" +seo_description: "gains.trade leads perp DEX security longevity at {{p50:gains}} days without a recorded exploit. GMX, Hyperliquid, dYdX v4, Lighter, Paradex ranked by days since last incident on rekt.news and public post-mortems." +subtitle: "Days each perp DEX has operated without a recorded security exploit, counted live from a versioned incident registry sourced from rekt.news and public post-mortems. Higher means a longer clean track record. Oracle-priced venues with immutable smart contracts have a structural advantage: there is no custodial hot wallet and no privileged admin key that can drain the vault." +category: Trading +status: live +metric: Days since last exploit +unit: count +higher_is_better: true + +disclaimer: | + This benchmark counts recorded incidents only. Absence from the registry does not prove a protocol is exploit-free: it means no incident matching the criteria (direct theft or loss of user funds via a security vulnerability) has been documented in the tracked sources as of the registry timestamp shown on each row. The registry is human-curated and updated when a new incident is confirmed. The registry date and source links are embedded in the harness. + +seo_intro: | + Smart contract exploits are the tail risk that every DeFi protocol carries. + For a perp DEX the consequences are asymmetric: a single reentrancy or + oracle manipulation event can wipe all liquidity from the vault while + positions are still open. The DeFi ecosystem has a public, reasonably + comprehensive ledger of these events in rekt.news and community + post-mortems. This benchmark converts that ledger into a live counter: + how many days has each venue operated without a recorded security exploit + that resulted in direct theft or loss of user funds via a protocol + vulnerability. gains.trade has been live since December 2021, more than + four years, with no recorded exploit in the tracked sources as of the + registry date. GMX had an exploit in July 2025 estimated at around 42 + million USD via a reentrancy vulnerability. Hyperliquid, dYdX v4, + Lighter and Paradex have no recorded protocol exploits. The harness + computes the counter live each day from a versioned YAML registry + committed to the harness repository. The registry is semi-automated: + a daily job polls rekt.news for new entries matching the venue slugs + and alerts a maintainer to confirm before committing. The counter + therefore reflects confirmed incidents, not unverified reports. + +abstract: | + The harness maintains a versioned incident registry (registry.yaml + embedded in the binary at build time). For each venue the registry + holds the protocol launch date and a list of confirmed security + incidents with date, estimated loss in USD, incident type and source + URL. The live counter is (current_timestamp - max(launch_date, + last_incident_date)) / 86400 in days. A venue with no incidents uses + the launch date as the start of the clean streak. The harness emits + three gauges per venue: perp_protocol_days_clean (the live counter), + perp_protocol_incidents_total (count of recorded incidents), and + perp_protocol_incident_amount_usd (cumulative USD lost in recorded + incidents). The registry includes only incidents that resulted in + direct theft or permanent loss of user funds via a protocol + vulnerability. Oracle price manipulation without smart contract + compromise, market-structure events (liquidation cascades, + governance disputes) and front-end phishing are excluded and noted + separately in the per-venue formula tooltip. + +methodology: + - "Registry: a versioned YAML file embedded in the harness binary at build time. Fields per incident: venue_slug, date (YYYY-MM-DD), amount_usd, type (exploit, oracle-manipulation, admin-key, bridge), source_url." + - "Counter formula: (Unix timestamp at query time - Unix timestamp of max(launch_date, last_incident_date)) / 86400. Emitted as perp_protocol_days_clean{venue}." + - "Incident criteria: direct theft or permanent loss of user funds via a documented smart contract vulnerability or privileged key compromise. Oracle manipulation without contract exploit is excluded. Market-structure events (liquidation cascades, large-position forced close) are excluded." + - "gains.trade: launched 2021-12-01. No incidents in tracked sources as of registry date 2026-08-02. Clean streak = days since 2021-12-01." + - "GMX v2: launched 2023-08-01. Incident 2025-07-09: reentrancy in vault and order-flow logic, estimated loss USD 42 000 000, source coinperps.xyz post-mortem. Clean streak = days since 2025-07-09." + - "Hyperliquid: launched 2023-11-01. No protocol exploit in tracked sources. Note: the March 2025 JellyJelly forced close was a market-structure event (large-position liquidation cascade triggering a governance emergency), not a smart contract exploit, and is excluded per criteria." + - "dYdX v4: launched on the Cosmos appchain 2023-10-01. No protocol exploit on v4. dYdX v1 and v2 on Ethereum had oracle manipulation events that are excluded here (different contract, different era)." + - "Lighter: launched 2023-07-01. No incidents in tracked sources." + - "Paradex: launched 2023-10-01. No incidents in tracked sources." + - "Registry update process: a nightly job polls rekt.news for new entries matching venue slugs and raises a review alert. A maintainer confirms and commits the updated registry.yaml. The harness auto-rebuilds and the counter updates on the next deploy cycle." + - "Survivorship note: older protocols have had more calendar time in which an exploit could occur. A long streak is a positive signal but is partly explained by age. The perp_protocol_incidents_total counter lets readers weight the streak against the full history." + +findings: + - "{{best_name}} leads the security longevity leaderboard at {{best_p50}} days without a recorded exploit (24h live counter)." + - "{{name:gains}} has operated since {{gains_launch}} with no recorded exploit in rekt.news or public post-mortems as of the registry date. The synthetic architecture (no custodial vault holding user funds outright, no privileged admin key) removes the most common attack surfaces." + - "{{name:gmx}} clocks {{p50:gmx}} days since its July 2025 reentrancy exploit (estimated USD 42 million). The incident involved a reentrancy in the vault and order-flow logic in GMX v2. The streak counter restarts from the incident date." + - "{{name:hyperliquid}}, {{name:dydx}}, {{name:lighter}} and {{name:paradex}} have no recorded protocol exploits. Their clean streaks start from their respective launch dates." + - "The perp_protocol_incident_amount_usd gauge shows cumulative confirmed losses per venue, allowing a risk-adjusted comparison alongside the day counter." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/perp-protocol-longevity + +prometheus: + window: 24h + expected_freshness_seconds: 86400 + freshness_metric: perp_protocol_days_clean + +faq: + - q: "What counts as a recordable incident?" + a: "Direct theft or permanent loss of user funds via a documented smart contract vulnerability or privileged key compromise. Oracle price manipulation events that do not involve a contract exploit are excluded. Market-structure events such as liquidation cascades triggered by large positions are excluded. The registry source URL for each entry is available in the harness YAML." + - q: "Why does gains.trade have such a long clean streak?" + a: "gains.trade launched in December 2021 and has not had a recorded exploit in the tracked sources as of the registry date, giving a streak of over four years. The synthetic architecture uses oracle pricing (no custodial AMM vault that can be drained) and immutable smart contracts without privileged admin keys on critical paths, which removes the most common attack surfaces." + - q: "Why is Hyperliquid's JellyJelly event not counted?" + a: "The March 2025 JellyJelly event was a market-structure incident: a large whale position was force-closed by the validator committee after a liquidation cascade threatened the insurance fund. No smart contract vulnerability was exploited and no user funds were stolen via a protocol bug. The methodology excludes market-structure events to keep the metric focused on smart contract security." + - q: "Does a long streak prove the protocol is safe?" + a: "No. A long clean streak is a positive signal and the best available public evidence of resilience under live conditions. It does not prove the code is exploit-free. Undiscovered vulnerabilities can exist for years before being found. The streak should be read alongside the protocol's audit history and architecture, not as a standalone guarantee." + - q: "How often is the registry updated?" + a: "A nightly job polls rekt.news and other public sources for entries matching the tracked venue slugs and raises an alert for manual review. A maintainer confirms and commits any new incidents, triggering a harness rebuild. The registry timestamp is visible in the harness source at registry.yaml." + - q: "Does this benchmark cover all historical incidents?" + a: "The benchmark covers incidents recorded in rekt.news and publicly disclosed post-mortems for the specific protocol deployments tracked. Incidents on predecessor versions (GMX v1, dYdX v1/v2), front-end phishing attacks and incidents on third-party integrations built on top of these protocols are out of scope." + +providers: + - slug: gains + name: gains.trade + tag: "Synthetic perps on Base, launched 2021-12-01, no recorded exploit" + formula: "Live counter: (current_unix - 2021-12-01T00:00:00Z) / 86400. No incidents in the registry as of 2026-08-02. Counter = days since launch." + queries: + p50: perp_protocol_days_clean{venue="gains"} + p90: perp_protocol_days_clean{venue="gains"} + p99: perp_protocol_days_clean{venue="gains"} + mean: perp_protocol_days_clean{venue="gains"} + success: perp_protocol_health{venue="gains"} + sample_size: count_over_time(perp_protocol_days_clean{venue="gains"}[24h]) + series: perp_protocol_days_clean{venue="gains"} + + - slug: lighter + name: Lighter + tag: "zk-rollup, launched 2023-07-01, no recorded exploit" + formula: "Live counter: (current_unix - 2023-07-01T00:00:00Z) / 86400. No incidents in the registry as of 2026-08-02. Counter = days since launch." + queries: + p50: perp_protocol_days_clean{venue="lighter"} + p90: perp_protocol_days_clean{venue="lighter"} + p99: perp_protocol_days_clean{venue="lighter"} + mean: perp_protocol_days_clean{venue="lighter"} + success: perp_protocol_health{venue="lighter"} + sample_size: count_over_time(perp_protocol_days_clean{venue="lighter"}[24h]) + series: perp_protocol_days_clean{venue="lighter"} + + - slug: dydx + name: dYdX v4 + tag: "Cosmos appchain, launched 2023-10-01, no recorded exploit on v4" + formula: "Live counter: (current_unix - 2023-10-01T00:00:00Z) / 86400. No incidents recorded for the v4 Cosmos appchain deployment. Counter = days since v4 launch." + queries: + p50: perp_protocol_days_clean{venue="dydx"} + p90: perp_protocol_days_clean{venue="dydx"} + p99: perp_protocol_days_clean{venue="dydx"} + mean: perp_protocol_days_clean{venue="dydx"} + success: perp_protocol_health{venue="dydx"} + sample_size: count_over_time(perp_protocol_days_clean{venue="dydx"}[24h]) + series: perp_protocol_days_clean{venue="dydx"} + + - slug: hyperliquid + name: Hyperliquid + tag: "HyperBFT L1, launched 2023-11-01, no recorded smart contract exploit" + formula: "Live counter: (current_unix - 2023-11-01T00:00:00Z) / 86400. No protocol exploit recorded. Note: the March 2025 JellyJelly forced close was a market-structure event, not a smart contract exploit, and is excluded per methodology." + queries: + p50: perp_protocol_days_clean{venue="hyperliquid"} + p90: perp_protocol_days_clean{venue="hyperliquid"} + p99: perp_protocol_days_clean{venue="hyperliquid"} + mean: perp_protocol_days_clean{venue="hyperliquid"} + success: perp_protocol_health{venue="hyperliquid"} + sample_size: count_over_time(perp_protocol_days_clean{venue="hyperliquid"}[24h]) + series: perp_protocol_days_clean{venue="hyperliquid"} + + - slug: paradex + name: Paradex + tag: "Starknet appchain, launched 2023-10-01, no recorded exploit" + formula: "Live counter: (current_unix - 2023-10-01T00:00:00Z) / 86400. No incidents in the registry as of 2026-08-02. Counter = days since launch." + queries: + p50: perp_protocol_days_clean{venue="paradex"} + p90: perp_protocol_days_clean{venue="paradex"} + p99: perp_protocol_days_clean{venue="paradex"} + mean: perp_protocol_days_clean{venue="paradex"} + success: perp_protocol_health{venue="paradex"} + sample_size: count_over_time(perp_protocol_days_clean{venue="paradex"}[24h]) + series: perp_protocol_days_clean{venue="paradex"} + + - slug: gmx + name: GMX v2 + tag: "Synthetics on Arbitrum, launched 2023-08-01, exploit 2025-07-09" + formula: "Live counter: (current_unix - 2025-07-09T00:00:00Z) / 86400. Incident: reentrancy in vault and order-flow logic, est. USD 42 000 000, source coinperps.xyz post-mortem 2025-07-09. Counter restarts from incident date." + queries: + p50: perp_protocol_days_clean{venue="gmx"} + p90: perp_protocol_days_clean{venue="gmx"} + p99: perp_protocol_days_clean{venue="gmx"} + mean: perp_protocol_days_clean{venue="gmx"} + success: perp_protocol_health{venue="gmx"} + sample_size: count_over_time(perp_protocol_days_clean{venue="gmx"}[24h]) + series: perp_protocol_days_clean{venue="gmx"} diff --git a/benchmarks/pm-api-latency.yml b/benchmarks/pm-api-latency.yml index b318746a..520b30a2 100644 --- a/benchmarks/pm-api-latency.yml +++ b/benchmarks/pm-api-latency.yml @@ -5,7 +5,7 @@ number: "038" title: Best prediction market API, ranked by live latency and uptime seo_title: "Best prediction market API 2026" seo_description: "Is the Polymarket API up? Live uptime and latency for Polymarket, Kalshi, Limitless, Manifold, Myriad, PredictIt and Smarkets price endpoints." -subtitle: Warm price endpoint latency of seven prediction market venue APIs from three regions, plus a live uptime panel that answers whether each API is up right now. +subtitle: Warm price endpoint latency of eight prediction market venue APIs from three regions, plus a live uptime panel that answers whether each API is up right now. category: Trading status: live @@ -17,11 +17,11 @@ seo_intro: | When the Polymarket API stops answering, the only pages that notice are downdetector clones aggregating complaints. None of them actually probe the API. This benchmark does. A harness in three regions requests the - price endpoint of Polymarket, Kalshi, Limitless, Manifold and Myriad - every 5 seconds over warm connections, records every round trip, and - publishes two things venue status pages do not: a latency leaderboard - ranking the best prediction market API by measured p50, and a per venue - uptime panel computed from the same probes. If you are here to check + price endpoint of Polymarket, Kalshi, Limitless, Manifold, Myriad, + PredictIt, Smarkets and Metaculus every 5 seconds over warm connections, + records every round trip, and publishes two things venue status pages + do not: a latency leaderboard ranking the best prediction market API + by measured p50, and a per venue uptime panel computed from the same probes. If you are here to check whether the Polymarket API is up right now, the uptime panel below is a direct measurement, refreshed continuously, not a complaint counter. If you are choosing which venue API to build on, the leaderboard ranks @@ -41,31 +41,29 @@ abstract: | this API up" is answered by direct measurement from three regions rather than by user reports. Samples that fail because our pinned market expired are classified probe_invalid and never count against the venue. - Third party data aggregators that resell venue prices (Codex) - are tracked separately in the pm-data-freshness bench and on - the data feeds tab of the prediction markets hub. This page ranks the - seven venues' own APIs only, so the leaderboard answers one clean - question: which prediction market venue API is fastest right now. + This page ranks the venues' own APIs only, so the leaderboard + answers one clean question: which prediction market venue API is + fastest right now. methodology: - - "Price endpoint per venue. Polymarket: CLOB `/midpoint`. Kalshi: `/markets/{ticker}`. Limitless: `/markets/{slug}`. Manifold: `/v0/market/{id}`. Myriad: `/markets/{slug}`. This is the hot path of real integrations: one market, one quote, polled in a loop." + - "Price endpoint per venue. Polymarket: CLOB `/midpoint`. Kalshi: `/markets/{ticker}`. Limitless: `/markets/{slug}`. Manifold: `/v0/market/{id}`. Myriad: `/markets/{slug}`. PredictIt: `/api/marketdata/markets/{id}` (15s interval, 5 req/min limit). Smarkets: `/v3/markets/{id}/`. Metaculus: `/api2/questions/{id}/`. This is the hot path of real integrations: one market, one quote, polled in a loop." - "Warm probes every 5s (7s on Manifold, whose API sits behind max-age=5 plus stale-while-revalidate=10) from us-east, eu-west and sgp over a keep alive pool. A separate cold connect probe once per minute records the TCP plus TLS handshake a brand new client pays." - "Every sample carries a cache flag read from cf-cache-status, x-cache and age headers. Latency aggregates exclude cache hits: a CDN hit measures the nearest edge, not the venue's API, and including Manifold's residual cache hits would flatter it by design." - "Uptime is the average of a per venue, per region health gauge (pmapi_health) computed by the harness from probe outcomes. 100 percent means every probe cycle in the window succeeded from that region. The 24h and 7d panels below are the live answer to whether each API is up." - "Outcome classification: ok, timeout (8s), http_4xx, http_5xx, throttled, probe_invalid, net_error. probe_invalid covers failures that are our pin's fault, such as a market that resolved intraday; those are excluded from both latency and uptime. The pinned market is re selected daily at 00:00 UTC and immediately on probe_invalid." - - "Division of labor with the sibling benches: how each venue behaves as request rates climb, including throttle onset and 429 handling, is measured in pm-rate-limits (bench 037). How fresh third party data providers relay Polymarket data is measured in pm-data-freshness (bench 032). This bench ranks the venues' own APIs on latency and uptime at a polite request rate." + - "Division of labor with the sibling benches: how each venue behaves as request rates climb, including throttle onset and 429 handling, is measured in pm-rate-limits (bench 037). WebSocket connect-to-snapshot and trade publication lag are measured in pm-ws-latency (bench 114). This bench ranks the venues' own APIs on latency and uptime at a polite request rate." - "Myriad's origin is a single region US East deployment behind Heroku, so its latency from eu-west and sgp is dominated by geography. Reported as measured, called out per region." - "All five venues are probed by the same open source harness as bench 037, from the same processes, so the two benches share one probe budget and one identifying User-Agent: `OpenChainBench/1.0 (+https://openchainbench.com/methodology; contact@openchainbench.com)`. We publish latency and uptime measurements only, never market data." - - "Aggregators that resell venue data (Codex) are deliberately excluded from this leaderboard. They are not venue APIs, they are relays on top of venue APIs, and ranking them next to the venues they relay would compare two different products on one axis. Their freshness lag against the Polymarket CLOB T0 stream is the relevant metric, measured in the pm-data-freshness bench. The prediction markets hub data feeds tab lists each aggregator with the venues it covers." + - "Data aggregators that resell venue prices are deliberately excluded from this leaderboard. They are relays on top of venue APIs, and ranking them next to the venues they relay would compare two different products on one axis. The prediction markets hub data feeds tab lists each aggregator with the venues it covers." - "Regions: us-east, eu-west, sgp (Railway). Histogram buckets 25ms to 10s." findings: - "{{best_name}} currently has the lowest warm price endpoint latency at {{best_p50}} p50 over the last 24h, making it the fastest prediction market API in the cohort by direct measurement." - - "All five venue APIs typically sit above 99 percent measured uptime; the gaps that do appear are short and regional, which is exactly what a complaint based status page cannot tell you. The uptime panels show the live 24h and 7d figures per venue." + - "All eight venue APIs typically sit above 99 percent measured uptime; the gaps that do appear are short and regional, which is exactly what a complaint based status page cannot tell you. The uptime panels show the live 24h and 7d figures per venue." - "{{name:manifold}} would look faster than it is if cache hits counted: its whole API sits behind a 5 second CDN cache. With cache hits excluded its origin answers at {{p50:manifold}} p50, an honest number a trading bot polling fresh quotes will actually see." - "{{name:myriad}} serves every region from a single US East origin, so its cohort worst p50 of {{p50:myriad}} is mostly geography. Switch the region dimension to us-east to see the API itself rather than the speed of light." - "{{name:polymarket}} answers its midpoint endpoint at {{p50:polymarket}} p50. Because we probe it every 5 seconds from three regions, the uptime panel doubles as a Polymarket API status check that updates continuously instead of waiting for user reports." - - "For builders who consume venue data through a managed relay, the relevant question is freshness lag, not request latency. Codex is measured in the pm-data-freshness bench (T0 against the Polymarket CLOB stream). The prediction markets hub data feeds tab lists each aggregator with the venues it covers." + - "For builders who consume venue data through a managed relay, the relevant question is freshness lag and WebSocket latency, not REST request latency. The pm-ws-latency bench measures trade publication lag on the Polymarket CLOB WebSocket. The prediction markets hub data feeds tab lists each aggregator with the venues it covers." disclaimer: "Uptime here means our probes succeeded from three specific regions at a polite request rate. A venue can be up for us and degraded for you, especially during regional network incidents, and a brief blip between probe cycles can go unrecorded. Treat the panel as a measured signal, not a guarantee." @@ -83,7 +81,7 @@ faq: - q: "Why do latency numbers exclude CDN cache hits?" a: "Because a cache hit measures the distance to the nearest edge node, not the venue's API. Manifold serves its entire API behind a 5 second cache and Kalshi's market list sits on CloudFront, so a naive average would mostly rank CDN proximity. Every sample carries a cache flag read from the response headers, and the headline aggregates only non cached, successful requests. The number you see is what a client polling for fresh quotes actually experiences at the origin." - q: "Does this benchmark cover throttling or data providers?" - a: "No, deliberately. What happens to each venue's API as request rates climb, including 429 onset and queueing behaviour, is its own benchmark, pm-rate-limits, which runs a careful daily ramp. And if you consume venue data through a provider such as Codex rather than calling the venue directly, the relevant number is relay freshness, measured in pm-data-freshness. This page answers two narrower questions: which venue API is fastest at a polite request rate, and is it up right now." + a: "No, deliberately. What happens to each venue's API as request rates climb, including 429 onset and queueing behaviour, is its own benchmark, pm-rate-limits, which runs a careful daily ramp. WebSocket latency and trade publication lag are covered in pm-ws-latency. This page answers two narrower questions: which venue API is fastest at a polite request rate, and is it up right now." per_chain_explainer: - slug: polymarket @@ -107,14 +105,16 @@ per_chain_explainer: - slug: smarkets h2: Smarkets API latency body: "Smarkets is a UK-regulated peer-to-peer betting exchange with a public CLOB order book endpoint. The harness probes /v3/markets/{id}/ for price (5s interval) and /v3/markets/{id}/quotes/ for the book (5s interval). No authentication is required for either. No visible CDN layer sits between the probe and origin, so latency reflects the API server response time directly. Smarkets is the only non-US venue in the cohort and its primary user base and server infrastructure are UK-centric, which affects the relative latency from US East vs EU West probe nodes." - + - slug: metaculus + h2: Metaculus API latency + body: "Metaculus is a public forecasting aggregator with no real-money markets, probed via its /api2/questions/{id}/ endpoint every 5 seconds from three regions. No authentication is required and no CDN cache layer was detected on the single-question endpoint. Metaculus has no order book and no WebSocket: all data access is REST. The uptime panel shows the live health gauge for the question API; the latency reflects the Django origin server without a CDN buffer. For builders querying aggregate community forecasts rather than market prices, Metaculus is the only venue in this cohort with a purely public read API at sub-second latency." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-rate-limits prometheus: window: 24h expected_freshness_seconds: 300 -rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets",conn="warm",class="price",cache!="hit"}[24h]))), "provider", "$1", "venue", "(.+)") +rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus",conn="warm",class="price",cache!="hit"}[24h]))), "provider", "$1", "venue", "(.+)") dimensions: venue: @@ -126,6 +126,7 @@ dimensions: - { value: myriad, label: Myriad } - { value: predictit, label: PredictIt } - { value: smarkets, label: Smarkets } + - { value: metaculus, label: Metaculus } region: - { value: all, label: All regions } - { value: us-east, label: US East } @@ -136,21 +137,21 @@ metric_panels: - id: uptime_24h label: Uptime 24h description: "Share of probe cycles that succeeded over the last 24 hours, averaged across the three probe regions. 100 percent means every 5 second probe of the venue's API came back healthy." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets"}[24h])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[24h])) label_key: venue unit: pct higher_is_better: true - id: uptime_7d label: Uptime 7d description: "Same health gauge averaged over 7 days. Short outages that vanish from the 24h figure stay visible here for a week." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets"}[7d])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[7d])) label_key: venue unit: pct higher_is_better: true - id: uptime_30d label: Uptime 30d description: "Same health gauge averaged over 30 days. Feeds the ledger's 30d window toggle so a one-hour outage that's already aged out of the 24h panel still shows up in the table over a month." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets"}[30d])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[30d])) label_key: venue unit: pct higher_is_better: true @@ -158,7 +159,7 @@ metric_panels: - id: cold_connect_p50 label: Cold connect description: "TCP plus TLS handshake time on the once a minute cold probe with keep alives disabled. The startup cost a brand new client pays before its first request." - metric: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_connect_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets"}[24h])) by (le)) + metric: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_connect_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[24h])) by (le)) label_key: venue unit: ms higher_is_better: false @@ -238,20 +239,20 @@ providers: series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="myriad",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) - slug: predictit name: PredictIt - tag: "CFTC no-action letter, US political markets only, probed at 15s due to 5 req/min rate limit" + tag: "CFTC no-action letter, US political markets only, 15s probe interval (5 req/min)" formula: "p50 of warm price requests at 15s intervals against /api/marketdata/markets/{id}, successful requests only, 24h window. Rate limit constraint means fewer samples than other venues." queries: - p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm"}[24h])) by (le)) - p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm"}[24h])) by (le)) - p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm"}[24h])) by (le)) - mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{venue="predictit",source="direct",class="price",conn="warm"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{venue="predictit",source="direct",class="price",conn="warm"}[24h])) + p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[24h])) success: clamp_max(sum(rate(pmapi_requests_total{venue="predictit",source="direct",class="price",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="predictit",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])), 1) sample_size: sum(increase(pmapi_requests_total{venue="predictit",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])) - series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm"}[1h])) by (le)) + series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="predictit",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) - slug: smarkets name: Smarkets - tag: "UK FCA-regulated CLOB exchange, public book endpoint, political and sports markets" + tag: "UK FCA-regulated CLOB, public book endpoint, political and sports markets" formula: "p50 of warm price requests against /v3/markets/{id}/, successful requests only, 24h window. Origin-level responses, no CDN cache." queries: p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="smarkets",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) @@ -261,3 +262,17 @@ providers: success: clamp_max(sum(rate(pmapi_requests_total{venue="smarkets",source="direct",class="price",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="smarkets",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])), 1) sample_size: sum(increase(pmapi_requests_total{venue="smarkets",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])) series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="smarkets",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) + + - slug: metaculus + name: Metaculus + tag: "Public forecasting platform, no auth, no order book, REST /api2/questions/" + formula: "p50 of warm round trips against the single question endpoint /api2/questions/{id}/, successful requests only, 24h window. No CDN cache layer detected." + queries: + p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) + mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[24h])) + success: clamp_max(sum(rate(pmapi_requests_total{venue="metaculus",source="direct",class="price",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="metaculus",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])), 1) + sample_size: sum(increase(pmapi_requests_total{venue="metaculus",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])) + series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) + diff --git a/benchmarks/pm-data-freshness.yml b/benchmarks/pm-data-freshness.yml deleted file mode 100644 index c6fb7bd1..00000000 --- a/benchmarks/pm-data-freshness.yml +++ /dev/null @@ -1,138 +0,0 @@ -# OpenChainBench. Bench № 032 - -slug: pm-data-freshness -number: "032" -title: Fastest prediction market data API, live freshness across venues -seo_title: "Fastest prediction market data API 2026" -seo_description: "Fastest prediction market data API ranked live across Polymarket and Kalshi. Milliseconds Codex and Predexon lag the venue gateway publish on top markets." -subtitle: Per event delay between provider arrival and the venue gateway publish, measured every minute on the top markets by 24 hour volume across Polymarket and Kalshi. - -category: Aggregators -status: live -metric: Freshness delta vs venue -unit: ms -higher_is_better: false - -seo_intro: | - Prediction markets generate the most time sensitive event stream in crypto. - An election market settles in seconds, a sports book moves on every play. - Builders that integrate Polymarket or Kalshi through a data provider - rather than hitting each venue directly need to know how many - milliseconds that provider adds between when the venue itself publishes - a trade and when the provider relays the same trade to its WebSocket - subscribers. This benchmark measures exactly that. The harness - subscribes to the same basket of top volume markets on each venue's own - canonical source (T0) and on Codex GraphQL subscriptions. Each trade - is cross correlated across the streams, and - the per provider lag versus the venue gateway publish time is recorded - as a Prometheus histogram. The leaderboard sorts by p50 freshness delta - in milliseconds, lower is better. Use the venue tab at the top of the - page to switch between Polymarket and Kalshi. - -abstract: | - Three WebSocket subscribers ride the same rotating basket of ~20 top - volume markets simultaneously on each venue. For every trade event - published on the venue's own canonical WebSocket gateway (the T0), we - record the moment it lands and the moment each provider relays the - same trade. The signature used to match a trade across providers is a - tuple based on the market identifier, price and size of the fill, and - a small time bucket, which is robust against the minor clock skew - between gateways. Providers that fail to relay a trade within 90 - seconds are not counted toward their p50, only toward their receive - total, so a provider can look fresh on the leaderboard while silently - dropping events. The success rate column flags that. Coverage today: - Codex on Polymarket and Kalshi. - -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." - - "Codex GraphQL subscriptions. `wss://graph.codex.io/graphql` with the `graphql-transport-ws` subprotocol. Firehose `onPredictionTradesCreated` subscription filtered client side to the venue's marketIds. Covers both Polymarket and Kalshi." - - "Basket. Top 20 active markets by 24h volume per venue, refreshed every 5 minutes. Polymarket pulls from `gamma-api.polymarket.com`, Kalshi pulls from `api.elections.kalshi.com/trade-api/v2/markets`." - - "Histogram buckets: 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, 60000 ms. The venue's own arrivals always land in the smallest bucket because by construction we measure other providers against it." - - "Region. `eu-west` (Railway europe-west4). Both venue gateways are geographically distributed; deltas reflect EU client to EU gateway latency." - - "Venue clock asymmetry. Polymarket CLOB trades carry no venue timestamp so T0 is when our harness receives the WS event; the Polymarket-to-harness RTT cancels inside the Codex delta. Kalshi's REST `create_date` is venue-side so the Codex Kalshi delta includes a Codex-to-harness RTT (50 to 100 ms) the Polymarket math cancels out. Kalshi rows are slightly stricter for the same provider, not more favorable; the gap is small versus the multi-second deltas the chart highlights." - -findings: - - "Each venue's own gateway is by definition the freshest source on the leaderboard for that venue. The lag versus its own publish time is on the order of the network round trip from the harness to the gateway, typically below 100 ms p50 from EU West." - - "{{name:codex}} sits at p50 {{p50:codex}} on the Polymarket tab because Codex indexes the chain event (Polygon block confirmation) rather than the off chain orderbook publish. The lag includes Polygon block time (~2s) plus ingestion. On Kalshi Codex ingests Kalshi's own WebSocket feed directly, so the delta is purely pipeline latency." - - "The spread between providers reflects integration depth: native gateway vs chain indexed pipeline. None of these is wrong, they answer different questions. For live trading UIs the gateway path is the only viable one." - -faq: - - q: "Which prediction market data API has the lowest latency right now?" - a: "{{best_name}} currently leads at {{best_p50}} (p50 over the last 24h) measured as time from the venue's own canonical WebSocket publish to provider relay. The leaderboard re sorts every minute on fresh Prometheus samples, so the answer reflects the actual measured lag on the active market basket, not a marketing claim. Switch the venue tab at the top to see Polymarket vs Kalshi independently." - - q: "What does 'freshness delta' mean for a prediction market API?" - a: "We connect to the venue's own canonical WebSocket and to the provider's WebSocket simultaneously, subscribe to the same markets, and for every trade event we record how many milliseconds the provider takes to relay the event after the venue itself publishes it. Lower is better. The venue's own gateway publish time is the canonical T0 because by construction nothing downstream can be faster than the source." - - q: "Why don't you include Polymarket REST polling on this benchmark?" - a: "When the Polymarket tab is selected, freshness is a WebSocket question. REST polling at 1s would have a floor freshness around 500ms (poll interval / 2) plus RTT, dominated by how often you poll. The Polymarket gateway WebSocket exists for exactly this reason, to avoid that floor. Adding REST as a row would make the leaderboard noisy without changing the conclusion: WebSocket beats polling by definition for real time data." - - q: "What does the Kalshi T0 source measure?" - a: "Kalshi's own public WebSocket at `wss://external-api-ws.kalshi.com/trade-api/ws/v2`, channel `trade`. The harness reads the `msg.ts_ms` field of each fill as the canonical publish timestamp and uses it as T0 for every Kalshi side comparison. Just like Polymarket's CLOB WebSocket on the Polymarket tab, it is the source against which provider latency is measured." - - q: "Are these numbers comparable to Kalshi or Limitless?" - a: "Kalshi is now its own tab on this page, click the Venue selector at the top to switch. Each venue uses its own canonical WebSocket as T0, so the absolute numbers are not directly comparable across tabs (different gateways, different network paths), but the relative provider ordering within each tab is honest. Limitless is not yet covered, we will add a tab if and when meaningful provider coverage exists for it." - - q: "How does OpenChainBench measure freshness?" - a: "Three WebSocket clients run in parallel inside the harness, all subscribed to the same basket of top volume markets on the active venue. Every 5 minutes we refresh the basket from the venue's own markets API. For each trade event, we compute a signature based on market id, price, size and a 5 second time bucket, and record the wall clock receive time on each provider. The freshness delta is `recv_provider - recv_venue` for the same signature. We export the histogram to Prometheus, the leaderboard reads the 24h p50." - -source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-freshness-bench - -prometheus: - window: 24h - expected_freshness_seconds: 300 - -# Venue selector. tabs at the top of the page. Server injects -# `venue="X"` into every PromQL query for the active tab. There is no -# "all" entry because cross venue averaging would mix two different -# canonical T0 gateways, which is not a meaningful comparison; the -# default landing tab is Polymarket (the first entry below). -dimensions: - venue: - - { value: polymarket, label: Polymarket } - - { value: kalshi, label: Kalshi } - -providers: - - slug: polymarket - name: Polymarket - tag: Native gateway WebSocket, no auth, sub 50ms publish latency - formula: "Polymarket gateway publish time is the canonical T0 on the Polymarket tab. The near zero row is the network round trip from the harness to the gateway, not a comparison against another source." - queries: - # Polymarket is T0 by construction on the Polymarket tab. Multiply - # the 0.5 ms floor by pm_health{provider="polymarket"} so the - # dimension framework's injected venue label gates the row: - # pm_health{provider="polymarket", venue="polymarket"} = 1 -> 0.5, - # pm_health{provider="polymarket", venue="kalshi"} = 0 -> 0, - # and the leaderboard hides empty rows. - p50: pm_health{provider="polymarket"} * 0.5 - p90: pm_health{provider="polymarket"} * 0.5 - p99: pm_health{provider="polymarket"} * 0.5 - mean: pm_health{provider="polymarket"} * 0.5 - success: avg_over_time(pm_health{provider="polymarket"}[24h]) - sample_size: sum(increase(pm_events_total{provider="polymarket"}[24h])) - series: pm_health{provider="polymarket"} * 0.5 - - - slug: kalshi - name: Kalshi - tag: Native venue REST, create_date is canonical T0 - formula: "Kalshi gateway publish time is the canonical T0 on the Kalshi tab. The near zero row is the network round trip from the harness to the Kalshi endpoint, not a comparison against another source." - queries: - # Symmetric to the Polymarket row, gated by pm_health{provider="kalshi"} - # so the venue label injected at runtime zeros out the row on the - # Polymarket tab. - p50: pm_health{provider="kalshi"} * 0.5 - p90: pm_health{provider="kalshi"} * 0.5 - p99: pm_health{provider="kalshi"} * 0.5 - mean: pm_health{provider="kalshi"} * 0.5 - success: avg_over_time(pm_health{provider="kalshi"}[24h]) - sample_size: sum(increase(pm_events_total{provider="kalshi"}[24h])) - series: pm_health{provider="kalshi"} * 0.5 - - - slug: codex - name: Codex - tag: Chain indexer on Polymarket, native ingestion on Kalshi - formula: "Median ms lag versus the venue gateway. On Polymarket Codex indexes the on chain Polygon settlement of each trade (block time ~2s plus ingestion). On Kalshi Codex ingests Kalshi's own WebSocket directly so the lag is pure pipeline latency." - queries: - p50: histogram_quantile(0.50, sum(rate(pm_freshness_delta_ms_bucket{provider="codex",kind="trade"}[24h])) by (le)) - p90: histogram_quantile(0.90, sum(rate(pm_freshness_delta_ms_bucket{provider="codex",kind="trade"}[24h])) by (le)) - p99: histogram_quantile(0.99, sum(rate(pm_freshness_delta_ms_bucket{provider="codex",kind="trade"}[24h])) by (le)) - mean: sum(rate(pm_freshness_delta_ms_sum{provider="codex",kind="trade"}[24h])) / sum(rate(pm_freshness_delta_ms_count{provider="codex",kind="trade"}[24h])) - success: clamp_max(sum(rate(pm_matched_total{provider="codex",kind="trade"}[24h])) / sum(rate(pm_events_total{provider="polymarket",kind="trade"}[24h])), 1) - sample_size: sum(increase(pm_matched_total{provider="codex",kind="trade"}[24h])) - series: histogram_quantile(0.50, sum(rate(pm_freshness_delta_ms_bucket{provider="codex",kind="trade"}[1h])) by (le)) diff --git a/benchmarks/pm-fee-comparison.yml b/benchmarks/pm-fee-comparison.yml deleted file mode 100644 index 42b34b35..00000000 --- a/benchmarks/pm-fee-comparison.yml +++ /dev/null @@ -1,74 +0,0 @@ -# OpenChainBench. Bench 112 - -slug: pm-fee-comparison -number: "112" -title: Prediction market fees, Polymarket vs Kalshi and more -seo_title: "Prediction market fees 2026: Polymarket, Kalshi, Limitless compared" -seo_description: "What does it cost to trade on Polymarket, Kalshi, Limitless and Myriad? Maker and taker fees, protocol cuts, total cost to trade, sourced from each venue's published fee schedule." -subtitle: Maker and taker fees, protocol cuts and net cost to trade across five prediction market venues, sourced from official fee schedules and verified from fills data. - -category: Trading -status: draft -metric: Effective taker fee -unit: pct -higher_is_better: false - -seo_intro: | - Nobody publishes a clean side-by-side of what it actually costs to trade prediction markets. Polymarket charges a flat percentage fee on each trade. Kalshi charges per-contract on a published schedule. Limitless embeds a fee in the AMM spread. Manifold uses play money. Myriad varies by market type. This bench parses each venue's fills data and official fee schedule so the comparison is based on measured values rather than marketing copy. - -abstract: | - Prediction market venues charge fees in ways that resist direct comparison. Polymarket charges 2% of - potential payout on every trade, regardless of contract price. Kalshi charges a per-contract fee on - a published schedule that scales with contract price and is capped at extremes, making near-certainty - contracts very cheap in percentage terms. Limitless charges fees through its CLOB spread. Manifold - uses play-money (mana) and has no real-money fee. Myriad charges a protocol fee on settlement. This - bench normalizes all fee structures to effective basis points of notional, measured at three - representative contract price points: at-the-money (50 cents), near-certainty (5 cents or 95 cents), - and mid-skewed (30 cents). The resulting leaderboard answers which venue is cheapest for a given - market condition, not just which venue has the lowest headline number. - -methodology: - - "Fee normalization: all fees are expressed as basis points of gross notional (contract price times contract count times $1 resolution value) at three price points: $0.50 (at-the-money), $0.05/$0.95 (near-certainty), and $0.30/$0.70 (mid-skewed). This converts Kalshi's per-contract dollar fee and Polymarket's payout-percentage fee to a common unit." - - "Polymarket fee measurement: the 2% fee is deducted from potential payout. A YES share bought at $0.50 pays $0.98 on resolution, not $1.00. Effective fee in basis points = 200 at all price points because the fee percentage is flat. No maker rebate exists on the public CLOB." - - "Kalshi fee measurement: Kalshi publishes a tiered per-contract fee schedule. Fees are capped at low and high price extremes. The harness reads the published schedule and computes the fee at each price point as fee/contract_price in basis points. Near-certainty markets benefit most from the cap." - - "Limitless and Myriad fee measurement: CLOB spread is used as a proxy for effective fee where explicit fee schedules are not published. The half-spread on the pinned market at each price tier is recorded as the effective round-trip cost." - - "Manifold is play-money and carries no real-money fee. It is included in the cohort for completeness but its fee row reads zero and it is excluded from the real-money fee rankings." - - "Data cadence: fee schedules are polled once per hour. Per-contract fees on Kalshi are re-read whenever the published schedule changes (detected via hash of the fee schedule endpoint). Effective fee rows on the leaderboard update within 1 hour of any venue publishing a fee change." - -findings: - - "Polymarket charges a flat 2% fee on each trade, one of the more transparent structures in the cohort since the rate does not vary by market or counterparty type." - - "Kalshi's per-contract fee means the effective percentage cost depends on where the contract trades relative to par. Fees are highest in percentage terms on near-certainty markets." - - "Manifold is play-money only. No real capital is at risk and no real fee is charged." - -disclaimer: "Fee schedules change without notice. Verify current fees directly with each venue before trading. This bench measures published and observed fees; it is not an endorsement of any venue." - -source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-fee-comparison - -faq: - - q: "What is the Polymarket trading fee?" - a: "Polymarket charges a 2% fee on each trade, deducted from the potential payout. If you buy YES shares at $0.50, each share pays out $0.98 rather than $1.00 if you win. The fee applies to both sides of the trade. There is no maker rebate on the public CLOB at the time of writing." - - q: "What is the Kalshi trading fee?" - a: "Kalshi charges a per-contract fee that scales with the contract's settlement price. The fee schedule is published on Kalshi's documentation. At mid-range prices ($0.40 to $0.60 per contract) the fee is typically around $0.07 per contract. Fees are capped, which makes very-low-price or very-high-price contracts cheaper in percentage terms." - - q: "Is Polymarket or Kalshi cheaper to trade?" - a: "It depends on the market. Polymarket's flat 2% fee is predictable. Kalshi's per-contract fee is cheaper on near-certainty markets where the cap kicks in, and more expensive on mid-book markets. The live fee comparison will show side-by-side numbers once the harness is running." - -providers: - - slug: polymarket - name: Polymarket - tag: 2% fee on potential payout, no maker rebate, flat across all price points - - - slug: kalshi - name: Kalshi - tag: Per-contract fee on published schedule, capped at price extremes, US-regulated - - - slug: limitless - name: Limitless - tag: CLOB venue, fee structure via spread, no published fee schedule - - - slug: manifold - name: Manifold - tag: Play-money (mana), no real-money fee, included for completeness - - - slug: myriad - name: Myriad - tag: Protocol fee on settlement, single-region US East diff --git a/benchmarks/pm-geographic-access.yml b/benchmarks/pm-geographic-access.yml deleted file mode 100644 index 8950a03c..00000000 --- a/benchmarks/pm-geographic-access.yml +++ /dev/null @@ -1,63 +0,0 @@ -# OpenChainBench. Bench 113 - -slug: pm-geographic-access -number: "113" -status: draft -title: Prediction market geographic access, which countries can reach each venue -subtitle: HTTP probes from regional nodes checking 200 OK vs blocked or redirect for Polymarket, Kalshi, Limitless, Manifold and Myriad, reported as percent of regions returning a successful response. -category: Trading -metric: Geographic accessibility rate -unit: pct -higher_is_better: true - -abstract: | - Prediction market venues operate under different regulatory regimes and enforce access restrictions - at the HTTP layer. Polymarket's international exchange has geoblocked US IP addresses since a 2022 - CFTC settlement. Polymarket US (QCX), a separate CFTC-regulated entity, launched in December 2025 - and is accessible to US users. Kalshi is CFTC-regulated and accessible to US users without - restriction. Minnesota enacted a law banning prediction markets effective August 1, 2026, which is - being challenged in court by the CFTC. This bench probes each venue's market listing endpoint from - nodes in 12 regions (US East, US West, EU West, EU Central, UK, Canada, Singapore, Japan, Australia, - Brazil, India, UAE) and records the HTTP response code. A 200 OK counts as accessible; a 403, - redirect to a block page, or connection reset counts as blocked. The result is expressed as the - percentage of probe regions returning a successful response. The bench does not assess legal access - rights, only observable HTTP-level behavior. - -methodology: - - "Probe design: the harness sends an HTTP GET to each venue's public market listing endpoint from dedicated probe nodes in 12 geographic regions once every 15 minutes. The request carries an identifying User-Agent and no VPN or proxy. Response codes, redirect chains and response body patterns are recorded." - - "Blocked classification: a response is classified as blocked if the HTTP status code is 403, if the response is a redirect to a URL containing terms such as block, geo, unavailable, or restricted, or if the TCP connection is reset before a response is received. Timeouts over 10 seconds are classified as unreachable rather than blocked." - - "Metric definition: the pct metric is the share of probe regions returning a 200 OK response to the venue's market listing endpoint in the trailing 24-hour window. A venue accessible from all 12 regions scores 100 pct. A venue blocked from all US probes but accessible from 9 of 12 regions scores 75 pct." - - "Polymarket disambiguation: the harness probes both polymarket.com (international exchange, geoblocked for US IPs) and the Polymarket US (QCX) endpoint separately and reports them as distinct rows. The combined accessibility row shows the union of regions that can reach either endpoint." - - "Legal access vs HTTP access: this bench measures what the API returns to an HTTP request, not what a person in a given jurisdiction is legally permitted to do. A 200 OK from a US probe on a venue's endpoint does not mean US users are legally permitted to trade on that venue. Consult venue terms of service and qualified legal counsel for legal access questions." - - "Regulatory events: when a regulatory event changes HTTP-level access (such as the Polymarket US geoblock in 2022 or the Minnesota ban in 2026), the probe series will reflect the change within one probe cycle (15 minutes). The bench changelog records the date and description of each regime change." - -findings: - - "Polymarket's international exchange (polymarket.com) geoblocks US IP addresses following a 2022 CFTC settlement in which Polymarket LLC paid a $1.4M fine. US probes receive a 403 or redirect. The international exchange scores below 100 pct by design on this bench." - - "Polymarket US (QCX), a separate CFTC-regulated entity, launched December 3, 2025 and is accessible to US users. It initially launched iOS-only with a waitlist; the waitlist dropped in May 2026." - - "Kalshi (Kalshi Exchange LLC) is CFTC-regulated and accessible to US users from all US probe regions. It is the primary regulated US-accessible real-money prediction market." - - "Minnesota enacted a law banning prediction markets effective August 1, 2026. The CFTC is challenging this law in court. The bench monitors Minnesota-region probes for HTTP-level changes as the legal challenge progresses." - -disclaimer: "Geographic access status changes as regulatory environments evolve. Verify current access directly with each venue before depositing funds. This bench measures API-level HTTP responses, not legal access rights." - -source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-geographic-access - -providers: - - slug: polymarket - name: Polymarket - tag: International exchange geoblocked for US IPs since 2022 CFTC settlement - - - slug: kalshi - name: Kalshi - tag: CFTC-regulated, accessible to US users, all regions - - - slug: limitless - name: Limitless - tag: CLOB venue, no documented geographic restrictions - - - slug: manifold - name: Manifold - tag: Play-money, US-accessible, no geographic restrictions - - - slug: myriad - name: Myriad - tag: Single-region US East, no documented geographic restrictions diff --git a/benchmarks/pm-rate-limits.yml b/benchmarks/pm-rate-limits.yml index 27e41b86..aa25ca11 100644 --- a/benchmarks/pm-rate-limits.yml +++ b/benchmarks/pm-rate-limits.yml @@ -4,8 +4,8 @@ slug: pm-rate-limits number: "037" title: Prediction market API rate limits, tested with a daily ramp seo_title: "Prediction market API rate limits 2026" -seo_description: "Prediction market API rate limits measured live: daily request ramp against Polymarket, Kalshi, Limitless, Manifold, Myriad." -subtitle: Warm latency on book, price and list endpoints of five prediction market venue APIs, plus a daily rate limit ramp that records added latency and throttle onset per request tier. +seo_description: "Prediction market API rate limits measured live: daily request ramp against Polymarket, Kalshi, Limitless, Manifold, Myriad, PredictIt, Smarkets and Metaculus." +subtitle: Warm latency on book, price and list endpoints of eight prediction market venue APIs, plus a daily rate limit ramp that records added latency and throttle onset per request tier. category: Trading status: live @@ -49,13 +49,13 @@ abstract: | second window. methodology: - - "Endpoint classes per venue. Polymarket: CLOB `/book`, `/midpoint`, Gamma `/markets`. Kalshi: `/markets/{ticker}/orderbook`, `/markets/{ticker}`, `/markets`. Limitless: `/markets/{slug}/orderbook`, `/markets/{slug}`, `/markets/active`. Manifold: `/v0/bets`, `/v0/market/{id}`, `/v0/markets`. Myriad: `/markets/{slug}`, `/markets`. Myriad exposes no order book endpoint; that absence is reported, not worked around." + - "Endpoint classes per venue. Polymarket: CLOB `/book`, `/midpoint`, Gamma `/markets`. Kalshi: `/markets/{ticker}/orderbook`, `/markets/{ticker}`, `/markets`. Limitless: `/markets/{slug}/orderbook`, `/markets/{slug}`, `/markets/active`. Manifold: `/v0/bets`, `/v0/market/{id}`, `/v0/markets`. Myriad, PredictIt, Metaculus: price and list only (no order book). Smarkets: `/v3/markets/{id}/quotes/`, `/v3/markets/{id}/`, `/v3/events/`." - "Pinned market per venue: most liquid, near the money (price between 0.15 and 0.85), expiring more than 24h out. Re pinned daily at 00:00 UTC and immediately on probe_invalid. A degenerate 0.99 book has a latency profile that does not represent real usage." - "Warm probes every 5s (7s on Manifold, whose entire API sits behind max-age=5 plus stale-while-revalidate=10; tighter spacing would only measure the cache). Cold connect probe once per minute with keep alives disabled, TCP plus TLS handshake recorded separately." - "Every sample records a cache flag from cf-cache-status, x-cache and age headers. Kalshi's market list is served from CloudFront with max-age=15, so its list latency measures the edge, not the API. Edge served samples (cache=hit) are excluded from every latency aggregate and reported through the cache label, so no venue ranks on its CDN." - "Outcome classification: ok, timeout (8s), http_4xx, http_5xx, throttled (429), probe_invalid, net_error. Latency aggregates include successful requests only; failures count toward the success rate. probe_invalid covers stale pins, e.g. Limitless serves a CDN cached 400 for four hours once a market expires. Those are our fault, never the venue's." - "Daily ramp: 60s per tier at rising requests per 10s window against the book endpoint, one venue at a time, one region at a time on disjoint UTC hours (02:00 us-east, 04:00 eu-west, 06:00 sgp)." - - "Ramp tier clamps: Polymarket and Kalshi 25/50/100 per 10s (at most 7 percent of Polymarket's documented 1500 per 10s book budget), Manifold 15/30/60 (500 req/min per IP documented), Limitless 10/20/40 (limits undocumented, so conservative). Kalshi stops at the first 429 since its token bucket is documented. Myriad is excluded: its keyless budget is 30 req per 10s and ramping it would only measure our own quota." + - "Ramp tiers: Polymarket + Kalshi 25/50/100 per 10s, Manifold 15/30/60, Limitless 10/20/40, Smarkets 10/25/50, Metaculus 5/10/20. Kalshi stops at first 429 (documented token bucket). Myriad excluded (keyless budget 30/10s). PredictIt excluded (5 req/min published limit)." - "Ramp abort guard: the run stops the moment throttled plus 5xx responses exceed 1 percent of any 10 second window. The headline ramp metric is p50 latency during the tier minus the warm baseline p50 of the previous hour in the same region, because venues behind Cloudflare queue bursts without ever returning 429, and added latency is the only honest throttle signal there." - "WebSocket panel covers venues with a public market WebSocket, which today means Polymarket only. Kalshi requires authentication for WS, Manifold and Myriad have none, Limitless uses socket.io. Absence is shown, not penalized." - "Myriad's origin is a single region US East deployment behind Heroku. Latency from eu-west and sgp is geography, reported as measured." @@ -79,7 +79,7 @@ faq: - q: "What is the Kalshi API rate limit?" a: "Kalshi documents tiered rate limits per access level, with the basic read tier around 20 requests per second at the time of writing. It is the only venue in this cohort with a documented token bucket. Our ramp stops at the first 429 on Kalshi out of respect for that documented contract, and the tier where it happens (or does not) is recorded in the ramp panel." - q: "Which prediction market has the fastest API?" - a: "{{best_name}} currently has the lowest warm book endpoint latency at {{best_p50}} (p50 over the last 24h). Note what this does and does not measure: it is the latency of the venue's own public API over a warm connection at a polite request rate, per region. It is not data freshness via third party providers, which is a separate benchmark (pm-data-freshness)." + a: "{{best_name}} currently has the lowest warm book endpoint latency at {{best_p50}} (p50 over the last 24h). Note what this does and does not measure: it is the latency of the venue's own public API over a warm connection at a polite request rate, per region. For data freshness and WebSocket latency, see the pm-api-latency and pm-ws-latency benches." - q: "Does Manifold have an API rate limit?" a: "Yes, 500 requests per minute per IP, documented, and Manifold explicitly welcomes bots as long as they stay on a single IP. Be aware that the whole API is served behind a cache with max-age=5 and stale-while-revalidate=10, so polling faster than every 6 seconds mostly returns cached responses. Our probes space out to 7 seconds per URL and label every sample that still comes back from cache." - q: "What are the Limitless API rate limits?" @@ -87,7 +87,7 @@ faq: - q: "How do you test rate limits without abusing the APIs?" a: "Four guardrails. Tiers stay well inside documented budgets where they exist (at most 7 percent of Polymarket's book allowance). Every run aborts as soon as throttled plus server errors exceed 1 percent of a 10 second window, and Kalshi stops at the first 429. Only one region ramps a venue at a time, on disjoint UTC hours. And every request carries an identifying User-Agent with a contact address, so a venue can reach us or filter us selectively. The measurement is throttle onset, not stress to failure." - q: "Why measure the venue APIs directly instead of a data provider?" - a: "They answer different questions. If you build directly on a venue, its native API latency and throttle behaviour set your floor, and that is what this benchmark measures. If you consume the venue through a data provider such as Codex or Predexon, what matters is how fresh the provider's relay is, which is measured separately in the pm-data-freshness benchmark. Both pages link each other so you can compare the paths." + a: "They answer different questions. If you build directly on a venue, its native API latency and throttle behaviour set your floor, and that is what this benchmark measures. For API latency at a polite rate, see pm-api-latency. For WebSocket connect and trade publication lag, see pm-ws-latency." per_chain_explainer: - slug: polymarket @@ -111,14 +111,16 @@ per_chain_explainer: - slug: smarkets h2: Smarkets rate limits body: "Smarkets showed no visible rate limiting during reconnaissance probes at up to 6 rapid requests per second. The daily ramp uses conservative tiers (10, 25, 50 requests per 10 seconds) with the standard abort guard. The CLOB quotes endpoint returns full bid-offer depth for the pinned market on every request. Unlike Polymarket which queues bursts under Cloudflare, Smarkets returns direct origin responses, so the ramp records actual latency scaling rather than queue inflation." - + - slug: metaculus + h2: Metaculus rate limits + body: "Metaculus does not document API rate limits. The harness uses the most conservative ramp in the cohort (5/10/20 requests per 10 seconds) with the standard abort guard. There is no order book endpoint and no WebSocket. Because Metaculus is a forecasting aggregator rather than a betting exchange, its API is designed for low-frequency access; polling faster than once per 5 seconds on a single question likely hits undocumented limits. Warm price endpoint p50: {{p50:metaculus}}." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-rate-limits prometheus: window: 24h expected_freshness_seconds: 300 -rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{cache!="hit",conn="warm",class="book"}[24h]) or rate(pmapi_request_duration_seconds_bucket{cache!="hit",conn="warm",class="price",venue=~"myriad|predictit"}[24h]))), "provider", "$1", "venue", "(.+)") +rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{cache!="hit",conn="warm",class="book"}[24h]) or rate(pmapi_request_duration_seconds_bucket{cache!="hit",conn="warm",class="price",venue=~"myriad|predictit|metaculus"}[24h]))), "provider", "$1", "venue", "(.+)") dimensions: venue: @@ -130,6 +132,7 @@ dimensions: - { value: myriad, label: Myriad } - { value: predictit, label: PredictIt } - { value: smarkets, label: Smarkets } + - { value: metaculus, label: Metaculus } region: - { value: all, label: All regions } - { value: us-east, label: US East } @@ -140,7 +143,7 @@ metric_panels: - id: ramp_added_latency label: Ramp added latency description: "Worst tier of the daily ramp: p50 latency during the burst minus the warm baseline p50 of the previous hour, same region. Venues that queue under load instead of returning 429 show up here." - metric: 1000 * max(max_over_time(pmapi_ramp_added_latency_seconds{}[24h])) + metric: 1000 * max(max_over_time(pmapi_ramp_added_latency_seconds{venue!="predictit",venue!="myriad"}[24h])) label_key: venue unit: ms higher_is_better: false @@ -231,7 +234,7 @@ providers: - slug: myriad name: Myriad - tag: No order book endpoint, no WebSocket, single region Heroku origin, keyless 30 req/10s + tag: No order book, no WebSocket, single region Heroku origin, keyless 30 req/10s formula: "p50 of warm full round trips against the single market endpoint (Myriad exposes no order book, so the price class is its hot path), successful requests only, 24h window. Cross region latency reflects the single US East origin." queries: p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="myriad",class="price",conn="warm"}[24h])) by (le)) @@ -267,3 +270,17 @@ providers: success: clamp_max(sum(rate(pmapi_requests_total{venue="smarkets",class="book",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="smarkets",class="book",conn="warm",outcome!="probe_invalid"}[24h])), 1) sample_size: sum(increase(pmapi_requests_total{venue="smarkets",class="book",conn="warm",outcome!="probe_invalid"}[24h])) series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="smarkets",class="book",conn="warm"}[1h])) by (le)) + + - slug: metaculus + name: Metaculus + tag: "Public forecasting platform, no auth, no book endpoint, ramp 5/10/20 per 10s" + formula: "p50 of warm price requests against /api2/questions/{id}/, successful requests only, 24h window. No book endpoint. Ramp at 5/10/20 per 10s (limits undocumented)." + queries: + p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="metaculus",class="price",conn="warm"}[24h])) by (le)) + p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="metaculus",class="price",conn="warm"}[24h])) by (le)) + p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="metaculus",class="price",conn="warm"}[24h])) by (le)) + mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{cache!="hit",venue="metaculus",class="price",conn="warm"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{cache!="hit",venue="metaculus",class="price",conn="warm"}[24h])) + success: clamp_max(sum(rate(pmapi_requests_total{venue="metaculus",class="price",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="metaculus",class="price",conn="warm",outcome!="probe_invalid"}[24h])), 1) + sample_size: sum(increase(pmapi_requests_total{venue="metaculus",class="price",conn="warm",outcome!="probe_invalid"}[24h])) + series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="metaculus",class="price",conn="warm"}[1h])) by (le)) + diff --git a/benchmarks/pm-resolution-delay.yml b/benchmarks/pm-resolution-delay.yml new file mode 100644 index 00000000..2ee3b434 --- /dev/null +++ b/benchmarks/pm-resolution-delay.yml @@ -0,0 +1,243 @@ +# OpenChainBench. Bench № 039 + +slug: pm-resolution-delay +number: "039" +title: Resolution delay, Polymarket vs Kalshi +seo_title: "Prediction market resolution delay live 2026" +seo_description: "Polymarket and Kalshi resolution time measured live: median delay from close to payout, broken down by category. Polymarket anchored on UMA ProposePrice, Kalshi on settlement_ts from the public API." +subtitle: "How long from market close to final settlement: Polymarket measured onchain via UMA ProposePrice to QuestionResolved, Kalshi via settlement_ts minus close_time from the public REST API. Auto-generated multivariate parlays excluded from Kalshi data." + +category: Trading +status: live +metric: Resolution delay +unit: s +higher_is_better: false + +seo_intro: | + The most asked question about prediction markets after "is it legal" is "how + long until I get paid". The answer circulating on social media for Polymarket + is that 93 percent of markets resolve within 2 hours. That number has no + published methodology and cannot be reproduced from Polymarket's own Gamma + API. This benchmark measures what can actually be observed: for Polymarket, + the time from the first onchain outcome proposal on UMA's optimistic oracle to + the onchain resolution, read directly from Polygon logs. For Kalshi, the time + from a market's scheduled close_time to the first poll at which its result is + non-null, measured every five minutes. Both venues are broken down by category + so the comparison is apples to apples. + +abstract: | + Polymarket: a listener follows the UMA CTF adapters on Polygon + (QuestionInitialized, QuestionResolved, QuestionReset) and the Optimistic + Oracle they call (ProposePrice, DisputePrice). Delay = first ProposePrice + block to QuestionResolved block, joined to Gamma for category tags. + + Kalshi: a poller hits the public /trade-api/v2/markets?status=settled endpoint + every 5 minutes. Delay = settlement_ts - close_time (both from API, exact, + no polling error). Categories resolved via /events/{event_ticker}. Auto-generated + multivariate parlay markets (Kalshi "Exotics" category) are excluded: they + settle algorithmically in seconds and are not prediction market questions. No auth required. + +methodology: + - "Polymarket anchor: the first OO ProposePrice block timestamp, not Gamma fields. Verified live: Gamma closedTime equals the QuestionResolved block timestamp (circular by construction) and endDate is a scheduled buffer most markets beat." + - "Polymarket sources: Polygon logs of the post-migration UMA CTF adapters (binary 0x65070BE9..., neg-risk 0x69c47De9...) and their Optimistic Oracle (0x2c0367a9...). Gamma supplies the market catalogue and category tags." + - "Polymarket join: questionID = keccak256 of the proposal ancillary data, verified against QuestionInitialized events." + - "Kalshi anchor: settlement_ts from the public markets API (exact timestamp, no polling error). Delay = settlement_ts - close_time. close_time is the scheduled market close, not the real-world event end." + - "Kalshi scope: traditional prediction markets only. Exotics (auto-generated multivariate parlays) are excluded by design: they auto-settle so fast they never appear in status=closed, so the watchlist only contains real market questions. Categories from /events/{event_ticker} (lazy cache): Sports→sports, Politics/Elections→politics, Crypto/Financials→crypto, everything else→other." + - "Anchor asymmetry: Polymarket's 2h UMA challenge window is protocol-enforced: no market can resolve in under 2h on the clean path. Kalshi has no such floor; traditional sports and politics markets settle in minutes to hours after close_time. The difference in distributions reflects both operational speed and architecture." + - "Categories: sports, politics, crypto, other, consistent across venues. Per-category rows are the honest unit of comparison because market types run different resolution windows." + - "Disputes (Polymarket only): a DisputePrice before resolution marks the market disputed. Disputed delays land in the disputed series and are excluded from the main category rows." + - "Pending backlog (Polymarket only): markets past their scheduled endDate with no onchain resolution, polled from Gamma every cycle." + - "Listener: 45s incremental Polygon log polling with a 7-day chunked backfill on restart. Kalshi poller ticks every 5 min. Both coexist in the same process." + +findings: + - "Polymarket across all markets: median onchain resolution delay {{p50:polymarket-all}} from first proposal to resolution, p99 at {{p99:polymarket-all}}. The within 2 hours share is in the table above, measured, and does not match the 93 percent figure that circulates without a methodology." + - "Kalshi across all markets (excluding auto-generated parlays): median resolution delay {{p50:kalshi-all}} from scheduled close_time to settlement_ts. Kalshi resolves internally: no onchain oracle, no mandatory challenge window. The difference vs Polymarket's median reflects both operational speed and Polymarket's protocol-enforced 2h minimum." + - "Categories are not comparable on a single median: sports, crypto and politics markets run different resolution processes on each venue." + - "Disputes (Polymarket only) are rare but expensive: disputed markets carry materially longer delays than the clean path." + +disclaimer: "Polymarket delay is measured from the first onchain outcome proposal to resolution (UMA challenge window, minimum 2h by protocol). Kalshi delay is settlement_ts - close_time (exact from API), where close_time is the scheduled market close, not the real-world event end. Kalshi auto-generated parlay markets (Exotics) are excluded. Both anchors are lower bounds on total user wait from the real-world event ending." + +faq: + - q: "How long does Polymarket take to pay out after a market ends?" + a: "Measured onchain, the median delay from the first outcome proposal to resolution is currently {{p50:polymarket-all}} across all markets, and redemptions are claimable immediately after resolution. The full distribution matters: p99 is {{p99:polymarket-all}}, and the per category rows show that sports, crypto and politics markets run on different verification windows. Add the time between the real world event ending and the first proposal, which is not measurable onchain." + - q: "How long does Kalshi take to resolve?" + a: "Kalshi resolves via its own internal process, not through an onchain oracle. The delay measured here is from a market's scheduled close_time to the first API poll (every 5 minutes) at which a result appears. The current median is {{p50:kalshi-all}}. This reflects Kalshi's operational resolution speed, not an onchain settlement time." + - q: "Do 93 percent of Polymarket markets resolve within 2 hours?" + a: "We could not reproduce that figure. The fields in Polymarket's public Gamma API cannot produce it: closedTime is written at the moment of resolution (circular) and endDate is a scheduled buffer most markets beat. Measured from the first onchain outcome proposal, the within 2 hours share is in the table above and has been materially below 93 percent since this benchmark started." + - q: "What happens when a Polymarket market is disputed?" + a: "A dispute on UMA's optimistic oracle resets the question and routes it through UMA token holder voting, replacing the normal verification window with a voting period measured in days. The disputed label separates those markets so clean path percentiles stay honest." + - q: "Why is my Polymarket market still unresolved?" + a: "Three common reasons: the underlying event has not concluded despite the scheduled end date; no one has submitted a proposal yet (the clock only starts at first proposal); or the market is in a UMA dispute, which adds days. The pending backlog gauge counts all markets past their end date without onchain resolution." + +per_chain_explainer: + - slug: polymarket-all + h2: Polymarket resolution — all markets + body: "All Polymarket markets combined, measured from first UMA ProposePrice to QuestionResolved on Polygon. The median is {{p50:polymarket-all}}. Sports dominates volume; crypto runs fastest; politics carries conservative verification windows. p99 at {{p99:polymarket-all}} reflects the long tail of disputes and postponed events." + - slug: polymarket-sports + h2: Polymarket sports resolution + body: "Sports markets dominate Polymarket volume and currently resolve at {{p50:polymarket-sports}} median from first proposal. Nearly all disputes happen here: contested endings and stat corrections make sports the category where the oracle's verification window earns its keep." + - slug: polymarket-politics + h2: Polymarket politics resolution + body: "Politics markets resolve at {{p50:polymarket-politics}} median, the slow end. Political outcomes often need an authoritative source to publish before anyone proposes, and verification windows are conservative because these markets carry the largest open interest." + - slug: polymarket-crypto + h2: Polymarket crypto resolution + body: "Crypto price markets resolve at {{p50:polymarket-crypto}} median, the fastest Polymarket category. The outcome is machine checkable against price feeds the moment the window closes, so proposals arrive promptly and short verification windows suffice." + - slug: kalshi-all + h2: Kalshi resolution — all markets + body: "All Kalshi markets combined, measured from scheduled close_time to API-observed finalization (poll every 5 min). The median is {{p50:kalshi-all}}. Kalshi resolves via internal processes, not an onchain oracle, so the anchor is the market's scheduled close rather than an onchain proposal." + - slug: kalshi-sports + h2: Kalshi sports resolution + body: "Kalshi sports markets resolve at {{p50:kalshi-sports}} median from scheduled close. Sports outcomes can require official stat verification, so delays vary with league reporting speed." + - slug: kalshi-politics + h2: Kalshi politics resolution + body: "Kalshi politics markets resolve at {{p50:kalshi-politics}} median from scheduled close. Political outcomes on Kalshi are resolved by Kalshi's internal operations team against authoritative sources." + - slug: kalshi-crypto + h2: Kalshi crypto resolution + body: "Kalshi crypto markets resolve at {{p50:kalshi-crypto}} median from scheduled close. Machine-checkable outcomes against price feeds allow for prompt resolution." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-resolution-delay + +prometheus: + window: 24h + expected_freshness_seconds: 900 + freshness_metric: pmres_resolutions_total + +metric_panels: + - id: pending_backlog + label: Pending backlog (Polymarket) + description: "Polymarket markets past their scheduled end date with no onchain resolution yet. The queue a waiting bettor experiences; oldest entries are usually postponed events, not oracle failures." + metric: sum(pmres_pending_markets{}) + label_key: category + unit: count + higher_is_better: false + - id: disputes + label: Disputes (Polymarket) + description: "Polymarket resolutions that went through UMA's dispute process since the listener's backfill window. Rare, concentrated in sports." + metric: sum(pmres_disputes_total{}) + label_key: category + unit: count + higher_is_better: false + +ledger_columns: + - { label: "p50 (median delay)", slot: p50 } + - { label: "p99", slot: p99 } + - { label: "Pending", panel: pending_backlog } + - { label: "Disputes", panel: disputes } + +providers: + - slug: polymarket-all + name: "Polymarket — All" + tag: Every resolved Polymarket market across categories + formula: "Median seconds from first OO ProposePrice block to QuestionResolved block on Polygon. Success = share resolved within 2 hours of proposal." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="polymarket"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="polymarket",le="7200"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket"}), 1) + sample_size: sum(pmres_resolutions_total{venue="polymarket"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket"})) + + - slug: polymarket-sports + name: "Polymarket — Sports" + tag: Game and match markets, the volume majority + formula: "Median seconds from first proposal to resolution for Polymarket sports markets." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="sports"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="sports"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="sports"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="polymarket",category="sports"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="sports"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="polymarket",le="7200",category="sports"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="sports"}), 1) + sample_size: sum(pmres_resolutions_total{venue="polymarket",category="sports"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="sports"})) + + - slug: polymarket-crypto + name: "Polymarket — Crypto" + tag: Price markets, machine checkable outcomes + formula: "Median seconds from first proposal to resolution for Polymarket crypto price markets." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="crypto"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="crypto"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="crypto"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="polymarket",category="crypto"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="crypto"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="polymarket",le="7200",category="crypto"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="crypto"}), 1) + sample_size: sum(pmres_resolutions_total{venue="polymarket",category="crypto"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="crypto"})) + + - slug: polymarket-politics + name: "Polymarket — Politics" + tag: Election and policy markets, conservative windows + formula: "Median seconds from first proposal to resolution for Polymarket politics markets." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="politics"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="politics"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="politics"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="polymarket",category="politics"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="politics"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="polymarket",le="7200",category="politics"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="politics"}), 1) + sample_size: sum(pmres_resolutions_total{venue="polymarket",category="politics"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="politics"})) + + - slug: polymarket-other + name: "Polymarket — Other" + tag: Culture, weather, science and everything else + formula: "Median seconds from first proposal to resolution for uncategorized Polymarket markets." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="other"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="other"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="other"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="polymarket",category="other"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="other"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="polymarket",le="7200",category="other"}) / sum(pmres_resolution_delay_seconds_count{venue="polymarket",category="other"}), 1) + sample_size: sum(pmres_resolutions_total{venue="polymarket",category="other"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="polymarket",category="other"})) + + - slug: kalshi-all + name: "Kalshi — All" + tag: All Kalshi markets, close_time to observed finalization + formula: "Median seconds from Kalshi market close_time to first API poll observing a non-null result (5 min polling interval). Success = share resolved within 2 hours of close." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="kalshi"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="kalshi",le="7200"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi"}), 1) + sample_size: sum(pmres_resolutions_total{venue="kalshi"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi"})) + + - slug: kalshi-sports + name: "Kalshi — Sports" + tag: Kalshi sports markets + formula: "Median seconds from Kalshi sports market close_time to observed finalization." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="sports"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="sports"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="sports"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="kalshi",category="sports"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="sports"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="kalshi",le="7200",category="sports"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="sports"}), 1) + sample_size: sum(pmres_resolutions_total{venue="kalshi",category="sports"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="sports"})) + + - slug: kalshi-politics + name: "Kalshi — Politics" + tag: Kalshi politics markets + formula: "Median seconds from Kalshi politics market close_time to observed finalization." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="politics"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="politics"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="politics"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="kalshi",category="politics"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="politics"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="kalshi",le="7200",category="politics"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="politics"}), 1) + sample_size: sum(pmres_resolutions_total{venue="kalshi",category="politics"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="politics"})) + + - slug: kalshi-crypto + name: "Kalshi — Crypto" + tag: Kalshi crypto markets + formula: "Median seconds from Kalshi crypto market close_time to observed finalization." + queries: + p50: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="crypto"})) + p90: 1000 * histogram_quantile(0.90, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="crypto"})) + p99: 1000 * histogram_quantile(0.99, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="crypto"})) + mean: 1000 * sum(pmres_resolution_delay_seconds_sum{venue="kalshi",category="crypto"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="crypto"}) + success: clamp_max(sum(pmres_resolution_delay_seconds_bucket{venue="kalshi",le="7200",category="crypto"}) / sum(pmres_resolution_delay_seconds_count{venue="kalshi",category="crypto"}), 1) + sample_size: sum(pmres_resolutions_total{venue="kalshi",category="crypto"}) + series: 1000 * histogram_quantile(0.50, sum by (le) (pmres_resolution_delay_seconds_bucket{venue="kalshi",category="crypto"})) diff --git a/benchmarks/pm-ws-latency.yml b/benchmarks/pm-ws-latency.yml index 992b388f..f66c26de 100644 --- a/benchmarks/pm-ws-latency.yml +++ b/benchmarks/pm-ws-latency.yml @@ -5,7 +5,7 @@ number: "114" title: Polymarket WebSocket latency, live connection and update speed from 3 regions seo_title: "Polymarket WebSocket latency 2026: connection time and update speed" seo_description: "How fast does the Polymarket WebSocket connect and deliver market updates? Live p50 from us-east, eu-west and Singapore measured continuously by OpenChainBench." -subtitle: Time from WebSocket dial to first market snapshot plus inter-message update rate on the Polymarket CLOB feed, probed from three regions 24 hours a day. +subtitle: Connect-to-snapshot latency, inter-message update rate, and trade publication lag on the Polymarket WebSocket feed (ranked) and Kalshi WebSocket (measured, not ranked), probed from three regions 24 hours a day. category: Trading status: live @@ -47,12 +47,12 @@ methodology: - "Regions: us-east (Railway us-east4), eu-west (Railway europe-west4), sgp (Railway asia-southeast1). The Polymarket CLOB WS endpoint is globally distributed via Cloudflare, so regional differences reflect edge proximity and routing, not server capacity." - "Keepalive: a background goroutine sends the literal text PING every 10 seconds and the venue responds with PONG. Sessions that stop receiving data are detected by the reconnect logic on the next PING timeout. Disconnect rate is exported as a counter." - "Pin: the harness targets the same market used by the pm-rate-limits bench (highest 24h volume binary market with last trade price between 0.15 and 0.85), refreshed daily at 00:00 UTC. The WS client re-subscribes automatically when the pin changes." - - "Metrics: pmapi_ws_connect_to_snapshot_seconds histogram and pmapi_ws_update_interarrival_seconds histogram, both labeled by venue, region and source. The same pm-rate-limits harness process that probes REST endpoints runs the WS session, so both REST and WS data share one Railway deployment per region." - - "Other venues: Kalshi WS (wss://external-api-ws.kalshi.com) requires RSA-PSS signed headers from a US KYC account and returns 403 from non-US IPs, making it not viable for a public three-region benchmark. Limitless uses socket.io over HTTP long-polling, not standard WebSocket. Manifold, Myriad, PredictIt and Smarkets have no public WebSocket endpoint." + - "Metrics: pmapi_ws_connect_to_snapshot_seconds and pmapi_ws_update_interarrival_seconds histograms, labeled by venue, region and source. The pm-rate-limits harness runs the WS session alongside REST probes, sharing one Railway deployment per region. Kalshi note: its ticker channel is delta-only (no snapshot on subscribe), so connect-to-snapshot measures time to the first price change, which depends on market activity." + - "Other venues: Kalshi WS (wss://api.elections.kalshi.com/trade-api/ws/v2) requires RSA-PSS signed headers from a US KYC account and returns 403 from non-US IPs, so it is measured from us-east only and excluded from the rank matrix. Limitless uses socket.io over HTTP long-polling, not standard WebSocket. Manifold, Myriad, PredictIt, Smarkets and Metaculus have no public WebSocket endpoint." findings: - - "Polymarket's CLOB WebSocket connects and delivers the first snapshot in {{p50:polymarket}} p50 (24h, all regions). This is the startup cost a new subscriber pays to go from a fresh dial to live market data." - - "Kalshi's WebSocket is probed from us-east only (RSA-PSS auth required, 403 from non-US IPs) and records {{p50:kalshi}} p50 connect-to-snapshot from the east US region. Its inter-arrival and disconnect metrics are in the panels below." + - "Polymarket's CLOB WebSocket connects and delivers the first orderbook snapshot in {{p50:polymarket}} p50 (24h, all regions). This is the startup cost a new subscriber pays to go from a fresh dial to live market data." + - "Kalshi's WebSocket ticker channel is delta-only: it does not send a market snapshot on subscription. The 'connect-to-snapshot' timing for Kalshi measures the time from dial to the first incremental price update, not to a full snapshot. On an active market this is typically 10 to 60s and reflects market activity level as much as connection speed. The inter-arrival and disconnect metrics below are more representative of the real-time feed health." - "Inter-arrival time on the feed (the steady-state update rate on an active market) is published as a metric panel below for both venues. The faster the inter-arrival, the more granular the real-time picture a subscriber gets." disclaimer: "Connect-to-snapshot includes Cloudflare edge latency and routing from each probe region. Numbers vary with network conditions and market activity. This bench measures observed end-to-end startup time, not venue-side processing time alone." @@ -75,7 +75,7 @@ prometheus: window: 24h expected_freshness_seconds: 120 -rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_ws_connect_to_snapshot_seconds_bucket{source="direct",venue=~"polymarket|kalshi"}[24h]))), "provider", "$1", "venue", "(.+)") +rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_ws_connect_to_snapshot_seconds_bucket{source="direct",venue="polymarket"}[24h]))), "provider", "$1", "venue", "(.+)") dimensions: region: @@ -99,6 +99,20 @@ metric_panels: label_key: venue unit: ms higher_is_better: false + - id: trade_pub_lag_p50 + label: Trade pub lag p50 + description: "Median delay between venue server-side trade creation and WS subscriber receipt. Polymarket: CLOB WS timestamp field vs receive time (all regions). Kalshi: REST create_date vs WS ticker receive time (us-east only). Lower is better." + metric: 1000 * histogram_quantile(0.50, sum by (venue) (rate(pmapi_ws_trade_pub_lag_seconds_bucket{source="direct",venue=~"polymarket|kalshi"}[24h]))) + label_key: venue + unit: ms + higher_is_better: false + - id: trade_pub_lag_p90 + label: Trade pub lag p90 + description: "90th percentile trade publication lag. The tail cost a subscriber pays waiting for the venue to push a confirmed trade. Kalshi us-east only." + metric: 1000 * histogram_quantile(0.90, sum by (venue) (rate(pmapi_ws_trade_pub_lag_seconds_bucket{source="direct",venue=~"polymarket|kalshi"}[24h]))) + label_key: venue + unit: ms + higher_is_better: false - id: disconnect_rate label: Disconnects 24h description: "Number of WebSocket disconnects recorded across all regions in the last 24 hours. Kalshi probes us-east only; Polymarket probes all three regions." @@ -110,7 +124,7 @@ metric_panels: providers: - slug: polymarket name: Polymarket - tag: Public no-auth CLOB WebSocket, globally distributed via Cloudflare, PING keepalive, 3 regions + tag: Public no-auth CLOB WebSocket, Cloudflare CDN, PING keepalive, 3 regions formula: "p50 of connect-to-snapshot time across all three regions (24h). Covers dial, TLS, HTTP upgrade and initial snapshot delivery." queries: p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_ws_connect_to_snapshot_seconds_bucket{source="direct",venue="polymarket"}[24h])) by (le)) @@ -123,8 +137,8 @@ providers: - slug: kalshi name: Kalshi - tag: RSA-PSS auth required, CFTC-regulated, probed from us-east only (403 from non-US IPs) - formula: "p50 of connect-to-snapshot time from us-east only (24h). Kalshi WS returns 403 from eu-west and sgp even with valid credentials, so cross-region comparison is not available." + tag: RSA-PSS auth required, CFTC-regulated, us-east only (403 from non-US IPs) + formula: "p50 of time-to-first-price-update from us-east only (24h). Kalshi's ticker channel is delta-only (no snapshot on subscribe), so this measures time from dial to the next price change — not an initial snapshot delivery." queries: p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_ws_connect_to_snapshot_seconds_bucket{source="direct",venue="kalshi",region="us-east"}[24h])) by (le)) p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_ws_connect_to_snapshot_seconds_bucket{source="direct",venue="kalshi",region="us-east"}[24h])) by (le)) @@ -140,7 +154,7 @@ per_chain_explainer: body: "Polymarket publishes a public CLOB WebSocket at wss://ws-subscriptions-clob.polymarket.com/ws/market with no authentication required. After subscribing with an assets_ids array, the feed delivers orderbook snapshots and price change events. The harness records connect-to-snapshot at {{p50:polymarket}} p50 (24h, all regions) and the inter-arrival distribution visible in the panels below. The feed is fronted by Cloudflare so regional latency differences are small. The PING keepalive every 10 seconds keeps idle connections alive. This is the only venue in the cohort with a standard, public, unauthenticated WebSocket." - slug: kalshi h2: Kalshi WebSocket - body: "Kalshi's WebSocket API at wss://api.elections.kalshi.com/trade-api/ws/v2 requires RSA-PSS-SHA256 signed authentication headers (KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP, KALSHI-ACCESS-SIGNATURE) and a US IP address. The us-east probe node connects with a keyed account and records connect-to-snapshot and inter-arrival metrics. The eu-west and sgp probe nodes return 403 even with valid credentials, so Kalshi's latency in the rank matrix reflects us-east only. The connect-to-snapshot number shown for Kalshi is therefore comparable to Polymarket's us-east column, not its global average." + body: "Kalshi's WebSocket API at wss://api.elections.kalshi.com/trade-api/ws/v2 requires RSA-PSS-SHA256 signed authentication headers (KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP, KALSHI-ACCESS-SIGNATURE) and a US IP address. The us-east probe node connects with a keyed account and records connect-to-snapshot and inter-arrival metrics. The eu-west and sgp probe nodes return 403 even with valid credentials, so Kalshi's latency in the rank matrix reflects us-east only. Important: Kalshi's ticker channel does not push a market snapshot on subscribe — it sends incremental price updates only. The 'connect-to-snapshot' metric for Kalshi therefore measures how long after subscribing the next price change arrives, which is driven by market activity rather than connection speed. On an active market this is 10–60s; on a quiet market it can be several minutes. The inter-arrival panel is the more meaningful indicator of the feed's real-time responsiveness." - slug: limitless h2: Limitless body: "Limitless does not expose a standard WebSocket. Its real-time channel uses socket.io, which runs over HTTP long-polling with a custom framing protocol that is not directly comparable to a native WebSocket dial-to-snapshot measurement. Limitless REST latency is in the pm-api-latency bench." @@ -156,3 +170,6 @@ per_chain_explainer: - slug: smarkets h2: Smarkets body: "Smarkets has no public WebSocket endpoint. The REST API at api.smarkets.com/v3 provides order book access without authentication. Smarkets REST latency is tracked in pm-api-latency." + - slug: metaculus + h2: Metaculus + body: "Metaculus has no WebSocket endpoint. It is a public forecasting platform accessed entirely via REST (/api2/questions/). For REST latency and rate limit measurements, see pm-api-latency and pm-rate-limits." diff --git a/benchmarks/polymarket-resolution-delay.yml b/benchmarks/polymarket-resolution-delay.yml deleted file mode 100644 index 9c058e1c..00000000 --- a/benchmarks/polymarket-resolution-delay.yml +++ /dev/null @@ -1,190 +0,0 @@ -# OpenChainBench. Bench № 039 - -slug: polymarket-resolution-delay -number: "039" -title: Polymarket resolution time, measured onchain -seo_title: "Polymarket resolution delay live 2026" -seo_description: "Polymarket resolution time live from Polygon: median delay from outcome proposal to onchain payout, sports vs politics vs crypto." -subtitle: Seconds from the first onchain outcome proposal to UMA resolution, measured per category from Polygon logs, with dispute rate and the live backlog of unresolved markets. - -category: Trading -status: live -metric: Resolution delay -unit: s -higher_is_better: false - -seo_intro: | - The most asked question about Polymarket after "is it legal" is "how long - until I get paid". The answer circulating on social media and repeated by - language models is that 93 percent of markets resolve within 2 hours. That - number has no published methodology, and the data Polymarket's own Gamma - API exposes cannot produce it: the closedTime field is written at the - moment of resolution (so measuring from it always yields zero) and endDate - is a scheduled buffer that most markets beat. This benchmark measures the - delay that can actually be observed: the time from the first onchain - outcome proposal on UMA's optimistic oracle to the onchain resolution of - the market, read directly from Polygon logs of Polymarket's UMA CTF - adapters. The table breaks the delay down by category, because sports, - crypto and politics markets run different verification windows, and a - single global median hides that. Dispute rate and the live backlog of - closed but unresolved markets are reported alongside. - -abstract: | - A listener follows Polymarket's UMA CTF adapters on Polygon - (QuestionInitialized, QuestionResolved, QuestionReset) and the - Optimistic Oracle they call (ProposePrice, DisputePrice). For every - resolved market, the recorded delay is the wall-clock time between the - block containing the first outcome proposal and the block containing the - resolution. Markets are joined to Polymarket's Gamma catalogue by - questionID (the keccak hash of the proposal's ancillary data, verified - against live events) and classified into sports, politics, crypto or - other. A dispute resets the question and routes it through UMA's - dispute process; those markets are counted separately so the dispute - penalty is visible instead of polluting the happy path percentiles. The - pending gauge counts markets past their scheduled end date that have not - resolved onchain yet, which is the backlog a bettor waiting for a payout - actually experiences. - -methodology: - - "Anchor: the first OO ProposePrice block timestamp, not Gamma fields. Verified live: Gamma closedTime equals the QuestionResolved block timestamp to the second (circular by construction) and endDate is a scheduled buffer that a majority of markets resolve before. Any 'resolves within X' claim computed from those fields is unfalsifiable." - - "Sources: Polygon logs of the post-migration UMA CTF adapters (binary 0x65070BE9..., neg-risk 0x69c47De9...) and their Optimistic Oracle (0x2c0367a9...), all address-configurable. Gamma supplies the market catalogue and category tags." - - "Join: questionID = keccak256 of the proposal ancillary data, verified against QuestionInitialized events. Neg-risk markets expose a different Gamma questionID; categories fall back to keyword classification for those, the delay itself is unaffected." - - "Categories: sports, politics, crypto, other, classified from Gamma tags plus keyword fallback. Categories run different UMA verification windows, so per category rows are the honest unit of comparison." - - "Disputes: a DisputePrice before resolution marks the market disputed and its delay lands in the disputed series. QuestionReset events are tracked so re-proposals do not create duplicate joins." - - "Pending backlog: markets past their scheduled endDate with no onchain resolution, polled from Gamma every cycle. This includes markets whose underlying event has not happened yet despite the date passing, so it is an upper bound." - - "Listener: 45s incremental Polygon log polling with a 7 day chunked backfill on restart, two public RPC endpoints with rotation and backoff. Counters re-count the backfill window after a restart; quantiles are computed on the cumulative histogram, which the backfill repopulates within minutes." - - "Delays are measured between block timestamps, so the resolution granularity is Polygon's block time (~2s), negligible against delays measured in minutes and hours." - - "What this does not measure: the gap between the real world event ending and someone submitting the first proposal. That interval is not observable onchain; the proposal is the first onchain signal that the outcome is known. Reported delays are therefore a lower bound on the total wait." - -findings: - - "Across all markets, the median onchain resolution delay is {{p50:all-markets}} from first proposal to resolution, with p99 at {{p99:all-markets}}. The within 2 hours share is on the table above, measured, and it does not match the 93 percent figure that circulates without a methodology." - - "Categories are not comparable on a single median: sports markets currently sit at {{p50:sports}}, crypto at {{p50:crypto}}, politics at {{p50:politics}}. Different market types run different UMA verification windows, which is why the leaderboard is per category." - - "Disputes are rare but expensive: the dispute counter on this page tracks how many resolutions went through UMA's dispute process, and disputed markets carry materially longer delays than the clean path." - - "The pending backlog gauge is the number a waiting bettor actually feels: markets past their scheduled end date with no onchain resolution yet. Its oldest entries are weeks old, usually markets whose underlying event was postponed rather than oracle failures." - -disclaimer: "Delay is measured from the first onchain outcome proposal, not from the real world event ending. The gap between event and proposal is not observable onchain, so total wait times experienced by users are at least the figures shown." - -faq: - - q: "How long does Polymarket take to pay out after a market ends?" - a: "Measured onchain, the median delay from the first outcome proposal to resolution is currently {{p50:all-markets}} across all markets, and redemptions are claimable immediately after resolution. The full distribution matters though: p99 is {{p99:all-markets}}, and the per category rows above show that sports, crypto and politics markets run on different verification windows. Add the time between the real world event ending and the first proposal, which happens offchain and is not measurable, so treat these figures as the floor." - - q: "Do 93 percent of Polymarket markets resolve within 2 hours?" - a: "We could not reproduce that figure, and the fields in Polymarket's public Gamma API cannot produce it: closedTime is written at the moment of resolution, so measuring from it always returns zero, and endDate is a scheduled buffer most markets beat. Measured from the first onchain outcome proposal to resolution on Polygon, the within 2 hours share is shown live in the table above and has been materially below 93 percent since this benchmark started. If you have a methodology that supports the 93 percent claim, we would genuinely like to see it." - - q: "What happens when a Polymarket market is disputed?" - a: "A dispute on UMA's optimistic oracle resets the question and routes it through the dispute process, where UMA token holders vote. That replaces the normal verification window with a voting period measured in days. Disputes are rare, the live dispute counter on this page tracks the measured rate, but when one hits a market you are waiting on, the delay moves from minutes or hours to days. The disputed label in our data separates those markets so the clean path percentiles stay honest." - - q: "Why is my Polymarket market still unresolved?" - a: "Three common reasons, in decreasing likelihood. First, the underlying event has not actually concluded, even if the market's scheduled end date passed; postponed games and extended deadlines dominate the pending backlog we track. Second, no one has submitted an outcome proposal to the oracle yet; the clock we measure only starts at the first proposal. Third, the market is in a UMA dispute, which adds days. The pending backlog gauge above counts all markets past their end date without onchain resolution." - - q: "How does Polymarket resolution actually work onchain?" - a: "Polymarket markets resolve through UMA's optimistic oracle on Polygon. Someone posts a bond and proposes the outcome (ProposePrice). A verification window follows during which anyone can dispute by posting a matching bond. If no dispute arrives, the proposal settles and Polymarket's UMA CTF adapter resolves the market (QuestionResolved), making winning shares redeemable. If a dispute arrives, UMA token holders vote. This benchmark reads exactly those events from Polygon logs, so the delays shown are the protocol's own timestamps, not estimates." - - q: "Which Polymarket categories resolve fastest?" - a: "The leaderboard above ranks categories by measured median delay, and the ordering is stable enough to plan around: crypto markets, which resolve against onchain or exchange prices, run the shortest verification windows, while politics markets sit at the long end. Sports land in between and dominate volume. The numbers update live, so check the table rather than trusting a snapshot quoted in an article, including this one." - -per_chain_explainer: - - slug: sports - h2: Polymarket sports resolution - body: "Sports markets dominate Polymarket's resolution volume and currently resolve at {{p50:sports}} median from first proposal. They are also where nearly all disputes happen: contested endings and stat corrections make sports the category where the oracle's verification window earns its keep. If you bet game markets, the practical wait is this row plus however long the league takes to make the result official, which happens before the onchain clock starts." - - slug: politics - h2: Polymarket politics resolution - body: "Politics markets resolve at {{p50:politics}} median from first proposal, the slow end of the cohort. Political outcomes often need an authoritative source to publish before anyone proposes, and verification windows are conservative because these markets carry the largest open interest. The headline 'Polymarket resolves in minutes' claims circulating online are sports and crypto numbers; this row is the honest expectation for election style markets." - - slug: crypto - h2: Polymarket crypto resolution - body: "Crypto price markets resolve at {{p50:crypto}} median, the fastest category. The outcome is machine checkable against price feeds the moment the window closes, so proposals arrive promptly and short verification windows suffice. High frequency up or down markets recycle continuously, which is why crypto contributes a large share of total resolutions despite modest open interest per market." - -source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-resolution-delay - -prometheus: - window: 24h - expected_freshness_seconds: 900 - freshness_metric: pmres_resolutions_total - -metric_panels: - - id: pending_backlog - label: Pending backlog - description: "Markets past their scheduled end date with no onchain resolution yet. The queue a waiting bettor experiences; oldest entries are usually postponed events, not oracle failures." - metric: sum(pmres_pending_markets{}) - label_key: category - unit: count - higher_is_better: false - - id: disputes - label: Disputes - description: "Resolutions that went through UMA's dispute process since the listener's backfill window. Rare, and concentrated in sports." - metric: sum(pmres_disputes_total{}) - label_key: category - unit: count - higher_is_better: false - -# Surface the two LIVE signals in the ledger so the page reads as a UMA -# state dashboard, not just a historical characterization. The p50 is -# statistically stable (cumulative histogram), so the live answers are -# what shifts day to day: how many markets are stuck waiting, and how -# many have hit UMA's dispute process. -ledger_columns: - - { label: "p50 (median delay)", slot: p50 } - - { label: "p99", slot: p99 } - - { label: "Pending", panel: pending_backlog } - - { label: "Disputes", panel: disputes } - -providers: - - slug: all-markets - name: All markets - tag: Every resolved market across categories - formula: "Median seconds from first OO ProposePrice block to QuestionResolved block, cumulative histogram over the listener's window. The success column is the share resolved within 2 hours of proposal, the number the 93 percent claim pretends to be." - queries: - 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: 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)) - - - slug: sports - name: Sports - tag: Game and match markets, the volume majority - formula: "Median seconds from first proposal to resolution for sports markets. Success column is the share within 2 hours of proposal." - queries: - 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: 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"})) - - - slug: crypto - name: Crypto - tag: Price markets, machine checkable outcomes - formula: "Median seconds from first proposal to resolution for crypto price markets. Success column is the share within 2 hours of proposal." - queries: - 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: 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"})) - - - slug: politics - name: Politics - tag: Election and policy markets, conservative windows - formula: "Median seconds from first proposal to resolution for politics markets. Success column is the share within 2 hours of proposal." - queries: - 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: 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"})) - - - slug: other - name: Other - tag: Culture, weather, science and everything else - formula: "Median seconds from first proposal to resolution for uncategorized markets. Success column is the share within 2 hours of proposal." - queries: - 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: 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/benchmarks/swellchain-rpc.yml b/benchmarks/swellchain-rpc.yml new file mode 100644 index 00000000..be5e4490 --- /dev/null +++ b/benchmarks/swellchain-rpc.yml @@ -0,0 +1,136 @@ +# OpenChainBench. Bench № 128 + +slug: swellchain-rpc +number: "128" +title: Fastest free Swellchain RPC, live no-key EVM endpoint latency +seo_title: "Fastest free Swellchain RPC 2026" +seo_description: "{{best_name}} leads free Swellchain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h). 3 no-key providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for the `eth_getBlockByNumber(\"latest\", false)` method against every free, no-key public Swellchain EVM endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Swellchain is an OP Stack L2 (chain 1923) operated by Swell Network, the restaking-native rollup built on EigenLayer, ETH gas token, ~2 s block cadence, blob calldata settlement on Ethereum. The 2026-08-02 audit confirms 3 clean keyless providers: Ankr (rpc.ankr.com/swell), Sentio (swell-mainnet.rpc.sentio.xyz), and Thirdweb (1923.rpc.thirdweb.com). Excluded: official alt.technology endpoint (401 Unauthorized without key), dRPC (no swell route), Alchemy (key required). + +abstract: | + Per-chain member of the RPC latency cluster, extended to Swellchain. + We measure the round-trip latency of a single, identical JSON-RPC call + (`eth_getBlockByNumber(\"latest\", false)`) against every no-key public + Swellchain endpoint that sustains continuous probing, 3 providers + at launch, every 60 seconds, from us-east, eu-west and Singapore. The + harness classifies every response (ok / http_err / jsonrpc_err / stale + / timeout) with a Swellchain-scaled staleness gap (20 blocks + at Swellchain's ~2 s block time). The cross-chain view + lives on the parent `rpc-capabilities` benchmark; this page is the + Swellchain-scoped answer with per-region breakdowns as a first-class + dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus `avg(quantile_over_time(...))`; per-region breakdowns are first-class on this page via the region tabs." + - "Payload: `{\"jsonrpc\":\"2.0\",\"id\":,\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\", false]}`. Plain HTTP POST, identical for every endpoint, no API key in any request. Non-cacheable by design: the rotating id defeats edge caches that would answer a bare head query without touching a node." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." + - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 20 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." + - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to `chain=\"swellchain\"`. Provider coverage at launch: 3 no-key endpoints." + +findings: + - "{{best_name}} currently leads free Swellchain RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Swellchain RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples. Use the region tabs to see the leader from the origin closest to your deployment." + - q: "Which Swellchain RPCs work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: Ankr (rpc.ankr.com/swell), Sentio (swell-mainnet.rpc.sentio.xyz), and Thirdweb (1923.rpc.thirdweb.com). The official Swell alt.technology endpoint requires authorization. Every listed endpoint was live-verified with a `eth_getBlockByNumber` POST returning a parsable response before inclusion." + - q: "Does the fastest Swellchain RPC change by region?" + a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." + - q: "How is Swellchain RPC latency measured here, technically?" + a: "One identical `eth_getBlockByNumber(\"latest\", false)` POST every 60 seconds against each provider from each of 3 regions, using the same plain HTTP client the rest of the RPC cluster uses. Wall-clock round-trip is recorded at millisecond precision; p50/p90/p99 are computed via Prometheus `quantile_over_time` over 24 hours. Responses are classified (`ok` / `http_err` / `jsonrpc_err` / `stale` / `timeout`) so an endpoint stuck on an old head or returning errors behind HTTP 200 is never ranked as fastest." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="swellchain"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: ankr + name: Ankr + tag: Ankr public RPC gateway for Swellchain + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Ankr's no-key swellchain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="swellchain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="ankr", chain="swellchain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="ankr", chain="swellchain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="ankr", chain="swellchain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="ankr", chain="swellchain"}) / sum(ocb:rpc_call:rate_24h{provider="ankr", chain="swellchain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="ankr", chain="swellchain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="swellchain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="swellchain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="swellchain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="swellchain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="swellchain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ankr", chain="swellchain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="ankr", chain="swellchain", region="sgp"}[1h]) + + - slug: sentio + name: Sentio + tag: Sentio RPC node for Swellchain + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Sentio's no-key swellchain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="sentio", chain="swellchain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="sentio", chain="swellchain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="sentio", chain="swellchain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="sentio", chain="swellchain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="sentio", chain="swellchain"}) / sum(ocb:rpc_call:rate_24h{provider="sentio", chain="swellchain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="sentio", chain="swellchain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="sentio", chain="swellchain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="sentio", chain="swellchain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="sentio", chain="swellchain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="sentio", chain="swellchain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="sentio", chain="swellchain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="sentio", chain="swellchain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="sentio", chain="swellchain", region="sgp"}[1h]) + + - slug: thirdweb + name: Thirdweb + tag: Thirdweb public RPC gateway for Swellchain + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Thirdweb's no-key swellchain RPC endpoint." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="swellchain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="thirdweb", chain="swellchain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="thirdweb", chain="swellchain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="thirdweb", chain="swellchain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="thirdweb", chain="swellchain"}) / sum(ocb:rpc_call:rate_24h{provider="thirdweb", chain="swellchain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="thirdweb", chain="swellchain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="swellchain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="swellchain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="swellchain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="swellchain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="swellchain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="thirdweb", chain="swellchain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="thirdweb", chain="swellchain", region="sgp"}[1h]) diff --git a/benchmarks/validator-yield-hyperliquid.yml b/benchmarks/validator-yield-hyperliquid.yml new file mode 100644 index 00000000..8285722b --- /dev/null +++ b/benchmarks/validator-yield-hyperliquid.yml @@ -0,0 +1,447 @@ +# OpenChainBench. Bench № 115 + +slug: validator-yield-hyperliquid +number: "115" +title: "Hyperliquid validator leaderboard: live net APR, uptime, commission" +seo_title: "Best Hyperliquid validators 2026: yield, commission, uptime ranked" +seo_description: "Live Hyperliquid validator leaderboard ranked by net APR. All 34 validators tracked: net yield in bps, commission rate, uptime %, delegated stake in USD. Updated every 5 minutes from /info validatorSummaries." +subtitle: Per-validator net yield (predictedApr.day × uptime) in basis points across all 34 Hyperliquid validators. Ranked by net APR; commission and uptime shown as secondary columns. +category: Blockchains +status: live +metric: Net yield +unit: bps +higher_is_better: true + +disclaimer: | + net_yield = predictedApr.day x uptimeFraction from /info validatorSummaries. No MEV layer at the validator level on Hyperliquid; the centralised sequencer captures order-flow value upstream. Jailed validators land at 0 bps regardless of published APR. APRs cluster at 200 to 225 bps across the active set; commission rate is the primary differentiator for delegators. + +seo_intro: | + Hyperliquid runs a permissioned validator set of ~30 active nodes. Unlike + Solana's ~3000-validator market where yield spreads across hundreds of bps, + every non-jailed Hyperliquid validator earns the same gross APR from the + protocol; the only differentiation is commission rate, uptime, and jail + risk. This page surfaces the full active set, ranked by net yield + (gross APR after commission and downtime drag), so a delegator can see + exactly which operators take the smallest cut and stay online. + + The data updates every 5 minutes from the Hyperliquid `/info + validatorSummaries` endpoint. Net yield is `predictedApr.day × + uptimeFraction`, expressed in basis points (1% = 100 bps). + +abstract: | + Every 5 minutes, the validator-yield harness POSTs `{"type": + "validatorSummaries"}` to `api.hyperliquid.xyz/info` and reads + `predictedApr.day` × `stats[day].uptimeFraction` per validator. Jailed + validators receive `net_yield_bps = 0` regardless of their published APR. + Commission is taken from the `commission` field (decimal fraction, e.g. + `0.04` = 4% = 400 bps). Delegated stake is the `stake` field divided by + `1e8` (HYPE token has 8 decimal places) then multiplied by the live HYPE + oracle price from `metaAndAssetCtxs`. All metrics land in Prometheus under + `ocb_validator_net_yield_bps{chain="hyperliquid", validator=, + name=