Polymarket's Hidden Rebalancing Move: Decoding PositionsConverted

Polymarket’s multi-outcome markets - “Who wins the 2028 election?”, “Which team takes the championship?” - hide a clever portfolio operation that almost no retail trader ever sees: the PositionsConverted event. It lets a holder of NO tokens across several outcomes collapse them into YES tokens plus a guaranteed collateral payout, in a single transaction.

We just turned on indexing for the polymarket.positions_converted table in our ClickHouse cluster, capturing every emission of this event since the NegRiskAdapter contract launched. Here’s what’s inside.

What is a NegRisk market?

A “negative risk” (neg-risk) market is Polymarket’s structure for events with N mutually exclusive outcomes, where exactly one outcome happens. Each outcome i has its own pair of conditional tokens: YES_i (pays 1 USDC if outcome i wins) and NO_i (pays 1 USDC if any other outcome wins).

Across the whole market, prices must satisfy:

sum(price(YES_i)) = 1.00 USDC
price(NO_i) = 1.00 - price(YES_i)

Holding NO_i is a bet against a specific outcome. Holding it for many outcomes simultaneously means you’re betting against most of the field - which is a position the protocol can simplify.

The conversion mechanism

Suppose you hold k NO tokens, one each for outcomes in some set S (with |S| = k), and a market with N total outcomes. Whichever outcome wins:

  • Winner is in S (probability you’re “wrong” on): one of your NO tokens pays 0, the other k-1 pay 1 USDC each → you get (k-1) USDC.
  • Winner is outside S: all k of your NO tokens pay 1 USDC each → you get k USDC.

The minimum value of this basket is (k-1) USDC, guaranteed. The remaining “upside” - the extra 1 USDC if the winner falls outside S - is exactly equivalent to holding 1 YES token for each outcome not in S.

The NegRiskAdapter contract lets you cash this insight directly. Calling convertPositions(marketId, indexSet, amount):

  1. Burns amount of each NO token for outcomes in S.
  2. Pays out (k - 1) × amount USDC of collateral.
  3. Mints amount of each YES token for outcomes not in S.

This is one-way. You cannot go from YES back to NO this way - the protocol won’t increase your collateral exposure on demand.

Why it matters

Without this operation, a trader who accumulated NO positions across many outcomes would need to sell each one individually on the order book, paying spread and price impact at every step. Conversion is a direct contract call against the protocol’s invariants, free of order-book slippage. Market makers use it constantly to rebalance inventory and free collateral that’s implicitly tied up in “long the field minus one” positions.

Reading the event

The PositionsConverted event carries four logical fields plus standard blockchain context. In the ClickHouse table polymarket.positions_converted:

ColumnTypeMeaning
stakeholderStringThe wallet performing the conversion
market_idStringThe neg-risk market identifier (not a single outcome’s CLOB token id)
index_setUInt256 (string)Bitmask of which outcomes’ NO tokens are supplied
amountUInt256 (string)Tokens converted, 6 decimals (USDC scale)
contract_addressStringAlways 0xd91e80cf2e7be2e162c6513ced06f1dd0da35296 (NegRiskAdapter on Polygon)
block_timestamp, transaction_hash, log_index, …Standard event-location columns

The index_set bitmask

index_set is a uint256 where bit i indicates that outcome i’s NO token is included in the conversion input. So:

  • index_set = 1 (binary 0001) → only outcome 0’s NO is supplied. You get YES tokens for outcomes 1, 2, …, and 0 × amount collateral.
  • index_set = 6 (binary 0110) → outcomes 1 and 2’s NOs supplied (k=2). You get YES for outcomes 0, 3, 4, … and 1 × amount collateral.
  • index_set = 127 (binary 1111111) in a 7-outcome market → all seven NOs supplied (k=7), returning 6 × amount USDC and no tokens. This is just a structured exit.

In SQL, recover k cheaply:

SELECT bitCount(toUInt256(index_set)) AS num_no_tokens_supplied
FROM polymarket.positions_converted

What the on-chain data shows

The NegRiskAdapter has emitted 3,167,335 PositionsConverted events between 2023-12-07 and the time of writing, across 27,792 unique markets and 12,906 unique stakeholder wallets. The footprint is much narrower than retail trading (the order-filled table covers ~600K wallets) - this is a tool used by a small, sophisticated cohort.

Most conversions are small basket rebalances

Distribution of k (number of NO tokens supplied per call) for 2025+ events:

kEventsAvg amount (USDC)
1751,061926
2363,561352
3308,819240
4257,533215
5223,76990
6-10586,218~60
11-20220,367~55
20+235,180mostly < 40

The k=1 case is interesting: it releases zero collateral (because k-1 = 0), so the call is purely a token swap - “trade my NO for outcome i into a bundle of YES for everything else.” The high average size (~$926) suggests these are large directional flips by market makers, not retail.

As k grows the average amount shrinks - long-tail markets with many outcomes have thinner liquidity per token, so traders convert in smaller chunks.

A few wallets dominate

Top 5 stakeholders by total amount converted in 2025+:

WalletEventsVolume converted (USDC)Markets
0xada2...eaab460,678$82.1M7,120
0xd218...b5c984,752$78.0M3,247
0xa027...e1d5166$45.6M42
0xe8dd...ec86393,408$37.8M5,439
0xa9d1...a39987$36.7M10

Two distinct profiles jump out:

  • Bots (0xada2..., 0xe8dd...): hundreds of thousands of small conversions across thousands of markets - automated inventory management.
  • Discretionary whales (0xa027..., 0xa9d1...): under 200 calls each but tens of millions in volume - large rebalances on a small number of conviction markets.

Monthly trend

MonthEventsVolume (USDC)Unique wallets
2026-06 (partial)149,556$30.1M132
2026-05417,677$59.3M318
2026-04442,050$64.1M2,271
2026-03404,953$78.3M5,263
2026-02264,601$62.8M1,251
2026-01264,913$105.5M1,251
2025-12179,817$73.0M1,113
2025-11144,804$59.2M998

January 2026 was the peak so far - $105M of NO baskets converted into YES + collateral in a single month. The recent rise in unique wallets per month (5,263 in March 2026) suggests the tooling for these conversions has become more accessible.

Querying it yourself

from core.clickhouse import PolymarketAccessor

with PolymarketAccessor() as db:
    df = db.query_df("""
        SELECT
            block_timestamp,
            stakeholder,
            market_id,
            bitCount(toUInt256(index_set)) AS k,
            toFloat64(amount) / 1e6 AS amount_usdc,
            (bitCount(toUInt256(index_set)) - 1) * toFloat64(amount) / 1e6 AS collateral_released_usdc,
            transaction_hash
        FROM polymarket.positions_converted
        WHERE block_timestamp >= '2026-06-01'
        ORDER BY amount_usdc DESC
        LIMIT 20
    """)

To join with market metadata, the market_id in positions_converted corresponds to the parent neg-risk market - not the per-outcome clob_token_id. Joining with polymarket.raw_market_meta requires the neg-risk market id field there (or grouping that table’s rows by their common parent).

What to do with it

A few research angles this table unlocks:

  1. Market-maker inventory cycles. Track conversions alongside polymarket_order_filled for the same wallet to see how MMs rebalance after directional flow.
  2. Implied probability snapshots. Every conversion is a revealed-preference signal that the stakeholder considers (k-1)/k USDC immediately better than holding the full basket - i.e., they believe P(winner ∈ S) > 1/k.
  3. Crowd sentiment shifts. A spike in conversions concentrated on a particular index_set for one market means a cohort of traders has collectively given up on certain outcomes.
  4. Liquidity quality signal. Markets with high conversion activity have active arbitrageurs enforcing price consistency across outcomes - usually a sign of healthy quoting on the order book too.

The NegRiskAdapter is one of the most under-analyzed surfaces of Polymarket because its activity doesn’t show up in standard trade feeds. With the table now indexed end-to-end, it’s a good time to start looking.


Background reading: Decoding Polymarket covers the broader CTF + NegRisk architecture in depth. The NegRiskAdapter contract on Polygon: 0xd91e80cf2e7be2e162c6513ced06f1dd0da35296.