Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions benchmarks/pm-ws-latency.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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."
- "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. Note on Kalshi: its ticker channel sends incremental price updates only (no state snapshot on subscribe), so the connect-to-snapshot metric measures time to the first price change rather than time to initial data, which depends on market activity."
- "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."

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–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."
Expand Down Expand Up @@ -138,7 +138,7 @@ 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."
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. Kalshi WS returns 403 from eu-west and sgp even with valid credentials."
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))
Expand All @@ -154,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."
Expand Down
7 changes: 5 additions & 2 deletions harnesses/pm-rate-limits/cmd/script/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@ var (
Help: "1 when the most recent primary-class probe returned ok, 0 otherwise.",
}, []string{"venue", "region", "source"})

// Polymarket sends an immediate orderbook snapshot (<1s); Kalshi's ticker
// channel is delta-only (no snapshot on subscribe), so first-frame latency
// reflects "time to next price change" which can be 10–300s on quiet markets.
wsConnectToSnapshot = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "pmapi_ws_connect_to_snapshot_seconds",
Help: "Time from WebSocket dial to the first market data frame after subscribing. Kalshi requires auth for WS and Myriad has none, so only venues with a public WS appear.",
Buckets: durBuckets,
Buckets: []float64{0.025, 0.05, 0.075, 0.1, 0.15, 0.25, 0.4, 0.6, 1, 1.5, 2.5, 4, 6, 10, 15, 30, 60, 120, 300},
}, []string{"venue", "region", "source"})

wsInterarrival = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "pmapi_ws_update_interarrival_seconds",
Help: "Gap between consecutive market data frames on the public WebSocket for the pinned market.",
Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120},
Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 180, 300, 600},
}, []string{"venue", "region", "source"})

wsDisconnects = promauto.NewCounterVec(prometheus.CounterOpts{
Expand Down
Loading