/consensus <category> [N] [K] computes a consensus signal over a freshly-fetched, category-specific PNL cohort: it pulls the top-N category leaderboard traders, fans out their positions, filters to their in-category bets, and surfaces bets where ≥K distinct cohort wallets agree. Async (the bot replies instantly and spawns a background job). Shipped 2026-06-05; replaced the short-lived tag-based tag_consensus.

For Agents

  • Command: /consensus <category> [N] [K] — defaults N=50, K=3.
  • <category> must be one of the 9 leaderboard categories (see category-leaderboard-api); otherwise rejected.
  • Module: crates/polymarket-fetch/src/pmv2/category_consensus.rs.
  • Tables (migration 20260605010000_pm_category_consensus.sql): pm_category_consensus_runs + pm_category_consensus_rows.
  • 10-min cache: a recent done run is served immediately; else the bot replies running consensus algo for {category}… and tokio::spawns the job.
  • No edge/band — the fetched traders are NOT in pmv2_traders; the signal is agreement-count + stake + their leaderboard PNL.

Flow

  1. Validate<category> must be one of the 9 leaderboard categories.
  2. Cache check — if a done run < 10 min old exists, serve its snapshot immediately.
  3. Else reply instantly running consensus algo for {category}… and tokio::spawn a background job that:
    1. Fetch the top-N category leaderboard traders by monthly PNL (category-leaderboard-api).
    2. Fan out their positions — bounded buffer_unordered(16) + a 30s per-wallet tokio::timeout.
    3. Filter to their in-category bets via gamma event_tags.
    4. Compute consensus — group by (condition_id, outcome_index), keep groups where ≥K distinct cohort wallets agree, rank by agreement-count then stake, take top 25.
    5. Persist to the run/rows tables.
    6. Push the snapshot to Telegram.

What the signal is (and isn’t)

No edge/band — distinct from the pmv2 cohort

These traders are pulled fresh off the category leaderboard and are NOT in pmv2_traders, so there is no trader_score / skill band / conviction. The signal is purely agreement-count + total stake + the traders’ leaderboard PNL. It is a “the month’s top category PNL earners agree on this bet” read, not a skill-weighted edge score.

Don't confuse with the other two consensus-shaped things

They look alike but the cohort source differs entirely.

category → gamma-tag mapping

The position filter (step 3.3) maps a leaderboard category to the gamma event_tags used to keep only in-category bets. It is identity for most, with three exceptions:

leaderboard categorygamma tag(s)
(most)identity (same slug)
culturepop-culture
economicseconomy, economics
mentionsNO clean tag → skip-filter fallback (no tag filter applied)

This mismatch is exactly why the 9 leaderboard categories are a separate namespace from gamma tags (see category-leaderboard-api and pmv2-gamma-tags-categorization).

Replaced the tag-based consensus

This REPLACED a short-lived tag_consensus

An earlier, briefly-shipped tag-based tag_consensus ran the consensus over the existing pmv2 cohort filtered by tag (a SQL query). It was removed in favor of this fetch-based version, which builds a fresh category-specific PNL cohort instead of reusing the pmv2 cohort.

Fan-out tuning

Initial 8/90s was too slow — a weather run looked stuck

The position fan-out started at 8 concurrent / 90s per-wallet timeout; a weather run took ~9.4 min and looked stuck. Tuned to 16 concurrent / 30s per-wallet timeout. (Same connect_timeout/per-item-timeout discipline as the silent-hang fix.)

Scope

Does NOT touch the /topbets or the pmv2 scoring algo — it is a self-contained command with its own tables.