Building a Polymarket Resolution dataset on Dune.

polymarket onchain data
← back
Ledger Header

AI DISCLOSURE: The idea and the prose was largely typed by a Human. However, AI was used to give a structure and a narrative to the blog post. You can find the different versions here.

1. Introduction

About a month ago, I started working on a framework to detect insider trading on Polymarket. I decided to use Dune as the data source, since it’s easier to verify it by building it from first principles than by relying on the Polymarket API. One of the main features I wanted to use was PnL per position. However, I couldn’t find a reliable source of this data. Multiple existing methodologies look reasonable, but break down the moment you run some basic sanity checks. The integrity of the PnL data is important to anything that’s built on top of it. Hence, I decided to delve deeper into building a reliable PnL dataset on Dune with an auditable ledger, straight from onchain data. This report is a documentation of the process: what are the obvious approaches, the flaws in them, where they break, and how to fix them. We use examples to pinpoint how each assumption breaks, and how the same example gets fixed at the end of the process. We will build an event-ledger that tracks all the onchain events associated with Polymarket positions, using it to reconstruct wallet-market level shares, USD flows, and PnL. We will normalize all events into a common accounting model, and the resulting ledger can be used beyond just PnL calculations and can be used to build downstream features such as execution behaviour and fill price dispersion.

2. The drawbacks of the obvious source

Choosing Dune as my data source was a major decision, esp when pretty much every other analytics framework from explorers to Twitter bots utilizes Polymarket’s API source. This decision is made worse by the fact that quite a lot of Dune’s prediction market data is now closed and available only to Enterprise customers.

Polymarket’s own API provides three endpoints that are, in general, used to pull the required data.

EndpointWhat it gives youWhat it’s missing
/v1/market-positionsCurrent position snapshotNo history, no cost basis
/positionsPosition snapshotNo trade-level breakdown, no spread
/closed-positionsFinal resolved snapshotNo entry price, no path taken to get there

The problem with these endpoints is that,

  1. They are closed-source and black-boxed
  2. What you get is what you have - you get a final state, no historical data, no 2nd-order analytics. (the same problem faced by every GraphQL endpoint)

Important info like entry and exit prices, fill spreads are condensed into a single avgPrice data point. One can assume they can circumnavigate the lack of historical data, utilizing the /trades endpoint. But as you will soon see, there is more to a position than trades alone. With respect to closed-source, building the dataset from onchain data gives the dataset verifiability, each action auditable, and the ability to combine it with other onchain data.

3. The naive methodologies on Dune

Now that I have chosen the source of Data, I look at existing works.

3.1. CLOB-only position reconstruction

The most common approach is to aggregate USD delta from CLOB trades, built on top of OrderFilled events, and combine it with USD withdrawn from PayoutRedemption events. As someone looking at the Polymarket data structure for the first time, this sounds like a sound methodology (insert Me).

A user can enter or exit positions through the CLOB and exit on Redemption.

A position in Polymarket is stored as an ERC-1155 token share. Since this is a token share, this share cannot be negative. In order to validate the above assumption, we hence try to compute the shares delta from each CLOB trade or OrderFilled event.

Let’s look at an example (it’s a chosen case for demonstration, and you will soon see this is a very common case), where the wallet 0xce296aaf92ecc022cc6608a54c622bb1c445b71b trades the Will Gemini 3.0 be released on November 17 2025? market. The market has two tokens:

Token SideToken
YES46687945077176076830096477597797725250961514733182621481405351828163193903577
NO113016318552201794810557514937858326971831314187777686552865771003364240784846

If you take a look at all the CLOB trades of the user in the market:

TimestampSharesToken SideDirectionShares DeltaShares CumulativeFlagToken PriceUSD VolumeTx Link
2025-11-14 19:45:32362.10NOBUY362.10362.100.952344.92🔗
2025-11-15 02:53:26100NOSELL-100262.100.96296.20🔗
2025-11-15 02:53:40100NOSELL-100162.100.96296.20🔗
2025-11-15 02:56:36162.1NOSELL-162.10.000.954154.64🔗
2025-11-15 03:29:28100YESSELL-100-100🚩0.0303.00🔗
2025-11-15 03:32:04250YESSELL-250-350🚩0.0307.50🔗

You can see how the 5th transaction sells a YES token that the user never bought.

Some approaches attempt to sidestep this by including wallets whose books will close cleanly using CLOB trades alone, i.e balances are non-negative. This is an approach that doesn’t explain or explore the consequences of this missed section.

A bigger issue with this methodology is that it doesn’t understand the trade being placed and the larger context behind the trade. To a new analyst, it looks like the user is selling YES tokens, but in reality, this trade was a 2nd leg of a trade where they were increasing their NO position. A slightly vague tell is that, if you look at the above example, you might notice that the trader buys NO around 95 cents and sells NO around 96 cents. This is a known yield farming strategy on Polymarket, where wallets buy tokens close to resolution to take advantage of the last bit of price movement, which is highly likely to close at 1 dollar. This wallet in particular has been making such trades quite frequently; thus the low-price selling is very odd.

3.2. ERC 1155 balance reconstruction

This is the default way that enterprise customers of Dune, very likely, get their position historical data. In now removed (closed-source) Spells, Dune reconstructs ERC 1155 balances by aggregating SingleTransfer and BatchTransfer event deltas. This is the correct way to reconstruct position balances. However, it misses a lot of key features, the most important being the USD invested in gaining the position and USD realized upon selling. This approach also doesn’t discern between a normal Wallet to Wallet transfer and a Trade or many of the other Ledger update events. One might suggest building this balance from ERC1155 transfer events, and then combining it with CLOB trades would solve the problem, but it still wouldn’t be enough.

This, on the other hand, would help us solve the problem of what all could possibly be missing outside of CLOB trades.

4. Reconstructing the CLOB

This is a section that can be skipped if your only concern is to recreate Resolution balances. If you are, however, interested in understanding fill events and what the onchain doesn’t tell you about the Fills, this section is for you.

Storm Slivkoff discovered in Polymarket Volume Is Being Double-Counted that a single market order triggers multiple OrderFilled events: one for each individual maker fill, and a single final OrderFilled event that summarizes all the fills. Let’s call this final OrderFilled event FullOrder. Analysts before that naively aggregated events, thus double-counting the volume. The standard OrderFilled event follows the following structure:

OrderFilled (
  index_topic_1 bytes32 orderHash,
  index_topic_2 address maker,
  index_topic_3 address taker,
  uint256 makerAssetId,
  uint256 takerAssetId,
  uint256 makerAmountFilled,
  uint256 takerAmountFilled,
  uint256 fee
)

The individual fills correctly post the makers and takers onchain. The final OrderFilled event, however, posts the taker wallet as the maker, and the maker is the Polymarket Market contract. This key distinction can be used to efficiently distinguish between individual fills and summary fills. This is the common pre-processing step to filter trades to avoid double-counting volume.

The article by Slivkoff also explains the 3 different types of CLOB trades:

  • Swap: A standard exchange of YES/NO tokens between the maker and taker, in exchange for USD.
  • Split: A taker and maker jointly deposit USD, and receive the same amount of YES/NO tokens respectively.
  • Merge: A taker and maker jointly deposit the same amount of YES/NO tokens, and receive USD.
OrderMatching
`OrderMatching` contract control flow

However, the article itself doesn’t delve deeper into finer details beyond that. This is probably because the aim of that article itself was to analyze overall volume processed, and spread wasn’t a focus for that.

Let’s check the following transaction 0x4fce56…93dc76. It’s the same transaction that is discussed in Slivkoff’s article. The transaction is a sale of YES tokens in the Kamala Harris replaced as nominee at DNC? market.

IndexMakerTakerMakerAssetTakerAssetMakerAmountFilledTakerAmountFilledSharesAmountPriceFillType
OnchainOnchainOnchainOnchainOnchainOnchainOnchainDuneDuneDune-
360xd9a591340776aced8f42b426dd1f42d0e79e8ce60x0c45c7c2b1ec281e2a8d0c204eb709f4bc9fba73USDYES28.4131803,157.0200003,157.020000$ 28.413180$ 0.09Swap
420x8e8cf968a888c72a45627be3660d1c815d4c66570x0c45c7c2b1ec281e2a8d0c204eb709f4bc9fba73NOUSD6,842.9800006,781.3931806,842.980000$ 6,781.393180$ 0.90Merge
440x0c45c7c2b1ec281e2a8d0c204eb709f4bc9fba730x4bfb41d5b3570defd03c39a9a4d8de6bd8b8982eYESUSD10,000.00000090.00000010,000.000000$ 90.000000$ 0.09FullOrder

A few takeaways from the broader structure based on examples such as above:

  1. We find that the OrderFilled event always contains one asset tokenID and the other is always the collateral, i.e USD.
  2. We also find that the OrderFilled events emit info from the maker’s perspective, i.e the asset tokenID, USD amount, and shares logged are the maker’s side of the transaction.

This is problematic when we deal with a Split or Merge trade, as the maker’s asset tokenID is opposite to the taker’s. While the shares remain constant, since equal amounts are required for Collateral movement, the USD amount logged is different. This is why the sum of USD logged in the individual fills does not match the USD logged in the final FullOrder OrderFilled event. When trying to analyze the spread of execution, we will only have the average price of the trade, similar to that of the Polymarket API.

To fix these issues (remember only for Split and Merge trades), we can get the taker’s token ID from the full order OrderFilled event associated with the transaction, and the taker’s price is computed as (1pmaker)(1 - p_{maker}) (since YES and NO are complementary tokens, the sum is 1 USD). Since the shares remain constant, the USD volume of the taker’s fill can be computed as:

USD volume=shares×(1pmaker)\text{USD volume} = \text{shares} \times (1 - p_{maker})

We can sanity check this formula to verify our Split and Merge labelling by comparing the aggregate sum of maker USD volume for fill orders with the aggregate sum of taker USD volume for full orders.

Now, our new CLOB contains the information of both the maker and taker side of the fill. Great. Now, from this we can build features like fill spread crossed, median execution price.

5. Non-CLOB Position Changes.

5.1. Splits and Merges

This is our answer to how 0xce296aaf92ecc022cc6608a54c622bb1c445b71b sold YES without buying them on CLOB. This also points out why the trade itself was missing context.

We already slightly explored what potentially was missing. Splits and Merges. Turns out, not only the CLOB matching engine, but also traders themselves can execute Splits and Merges. A trader can deposit Collateral into a market - minting YES/NO pairs. Then the trader can sell the NO or YES into the market to get a complementary position. This is a non-CLOB Split. The opposite is also true: where a trader can buy a position, pair it with a complementary position in their balances to withdraw collateral. This is a non-CLOB Merge.

Looking back at the 0xce296aaf92ecc022cc6608a54c622bb1c445b71b Will Gemini 3.0 be released on November 17 2025? market example: Here is how the actual swap looks.

TimestampSharesToken SideDirectionShares DeltaShares CumulativeFlagToken PriceUSD VolumeTx Link
2025-11-14 19:45:32362.10NOBUY+362.10362.100.952344.920xb5c4db.. 🔗
2025-11-15 02:53:26100NOSELL-100262.100.96296.200xc992ae.. 🔗
2025-11-15 02:53:40100NOSELL-100162.100.96296.200x4b67a5.. 🔗
2025-11-15 02:56:36162.1NOSELL-162.10.000.954154.640x8c274a.. 🔗
2025-11-15 03:26:24100YESSPLIT+100100.000.550.000xa5e792.. 🔗
2025-11-15 03:26:24100NOSPLIT+100100.000.550.000xa5e792.. 🔗
2025-11-15 03:29:28100YESSELL-10000.0303.000x76e3dc.. 🔗
2025-11-15 03:26:24250YESSPLIT+250250.000.550.000x7ba9db.. 🔗
2025-11-15 03:26:24250NOSPLIT+250350.000.550.000x7ba9db.. 🔗
2025-11-15 03:32:04250YESSELL-25000.0307.500x424240.. 🔗

Before the 100 and 250 share sells of the YES token, the trader executed SPLIT, where they deposited collateral to mint YES+NO pairs, thus creating a NO position. This is what I meant by the trade itself lacking context. In a previous work of mine, I labelled trades as Yield Farming and Notional Farming based on the price of execution of the trade. The SELLs of YES token would have been flagged as Notional Farming, since the token price was low, while in truth the trade was a second leg of a potential Yield Farming trade. The trader would go on to execute one more SPLIT + YES sale before they SELL their accumulated 350 share NO position.

It’s obvious how quickly these non-CLOB changes can degrade your dataset.

  1. Negative balances or Ghost sales - Where a trader seems to sell a position which they didn’t own.
  2. Zero balances post-buying - Where a trader just executed a buy, but their position exposure is zero, without a single sale.
  3. Merges involve withdrawing collateral - hence an increase in PnL is missed.
  4. Splits involve depositing collateral - hence a decrease in PnL is missed. Both of these affect PnL strongly, introducing negative PnL from negative balances, and positive PnL from Zero balances.

5.2. Conversion

This is a special feature of NegRisk Markets. If you are all the way here and do not know what a NegRisk market is, it’s an efficient way of bundling markets with mutually exclusive outcomes together. A presidential election market is a good example, as there can be 10 candidates. Instead of 10 different markets, we have a single market. A key feature of these markets is that, if there are n different outcomes, then 1 resolves to YES and n-1 resolves to NO. An extension of this is that, if anyone holds more than 1 NO token, they are guaranteed to resolve all but one NO token. Due to this, Polymarket allows anyone to convert n NO positions to USD worth n-1 NO positions by using the convertPositions function.

5.3. External ERC-1155 transfers

At the end of the day, Polymarket positions are ERC-1155 tokens that the holder fully controls, and hence can transfer them on their own volition. This means certain users try to transfer positions to other accounts. These must also be factored in. These can be performed via both SingleTransfer and BatchTransfer events.

6. Assembling the Ledger Dataset

6.1 Objective of the Dataset

Our ledger dataset should be of the following structure:

FieldDescription
WalletWallet whose position changes
MarketPolymarket market or condition
Outcome tokenYES, NO, or another outcome token
Event typeTrade, split, merge, conversion, or transfer
Share deltaChange in the wallet’s outcome-token balance
USD deltaCollateral spent or received
Transaction hashOn-chain transaction identifier
Event indexPosition of the event within the transaction
Block timeTimestamp of the event

Our main north star should be that at all points, the running sum of all share deltas must be non-negative.

We need to

  1. Unify the structure of the events:
    1. Find the correct trader wallet.
    2. Calculate the correct share delta.
    3. Price the asset and calculate the correct USD delta.
  2. Bring together all the components of the ledger:
    1. CLOB trades
    2. Standalone Splits and Merges
    3. NegRisk Converts
    4. Standalone Transfers
  3. We need exactly one row per tokenID moved.

6.2. CLOB Trades

In the Reconstructing CLOB trades section, we computed the taker side of CLOB trades. However, for Ledger events, we only need the maker side. This is because the FullOrder trade captures the taker-side delta as the maker. On the basis of this assumption, we can simply get the Token ID, Share delta, and USD delta from the CLOB trades dataset. The USD delta and share delta are negative if the trade is a Sell, positive otherwise. The pricing is also straightforward as P=usd/sharesP = usd/shares

6.3. Standalone Splits

1USD=1YES+1NO1 \text{USD} = 1 \text{YES} + 1 \text{NO}

Standalone splits are detected by PositionSplit events that do not have an OrderFilled event in the same transaction. The reasoning is that PositionSplit events associated with OrderFilled are already accounted for in the CLOB trades; hence, we avoid them to avoid double-counting. The wallet associated with the split is detected by the adjacent BatchTransfer event, which transfers all the YES + NO token pairs associated with that market. In the BatchTransfer event, the trading wallet is the recipient of the split tokens. Based on protocol source code, the adjacent BatchTransfer event is always emitted right before or right after the PositionSplit event, i.e if i is the index of the PositionSplit event, then i-1 or i+1 is the index of the adjacent BatchTransfer event. This is because Normal CTFs and NegRisk CTFs have slightly different event emission styles. For a Normal CTF, the flow is BatchTransfer to the wallet calling the split function, followed by the PositionSplit event. For a NegRisk, the wallet calling the split function is the NegRiskAdapter contract, which then transfers the YES + NO tokens to the trading wallet.

Since pricing involves USD to Token conversion, USD delta is negative, and Token delta is positive.

6.3.1. Pricing the trade

Since an equal amount of YES and NO tokens are minted, we can price the assets as 0.5 USD per token. This is a rather naive pricing logic. The correct pricing logic would require us to either:

  1. match the 2nd leg of the trade (or )
  2. have a standard pricing for YES/NO tokens.

We do not go down these approaches cause:

  1. Pricing the assets based on the USD value of the 2nd leg requires the existence of this 2nd leg, which is not always guaranteed.
  2. Standard pricing of YES/NO tokens itself is a can of worms, cause you need to define the price - is it the mid Price of CLOB, or the last executed price?

eg: Say a trader wants to gain a position of 10 YES tokens. They deposit 10 USD and mint 10 YES tokens + 10 NO tokens. The 2nd leg of the trade would be to sell the 10 NO tokens on the market. If they sell the 10 NO tokens for 0.1 USD per token, their recuperation is 1 USD (10 NO tokens * 0.1 USD per token). Thus, the effective cost of the 10 YES position is 0.9 USD.

Our naive approach manages to match the expected pricing, but offsets the effective cost of YES tokens to the NO tokens. Instead of a single 9 USD for 10 YES tokens, we get 5 USD for 10 YES tokens and 5 USD for NO tokens sold on the market for 1 USD. Instead of a 9 USD YES position at 0 USD profit, we get two positions - a YES position at 4 USD profit and a NO position at 4 USD loss.

6.4. Standalone Merges

1YES+1NO=1USD1 \text{YES} + 1 \text{NO} = 1 \text{USD}

Standalone merges are detected by PositionMerge events that do not have an OrderFilled event in the same transaction. The reasoning is the same as for standalone splits. Similar to standalone splits, the BatchTransfer event is used to detect the trading wallet associated with the merge, and the trading wallet is the sender of the merging tokens. Based on protocol source code, the BatchTransfer event is always emitted 2 events before the PositionMerge event, i.e if i is the PositionMerge event index, then i - 2 or i-3 is the BatchTransfer event index. The two different indexes are to account for Normal CTFs and NegRisk CTFs having slightly different event emission styles. Similar to standalone splits, we can price the assets as 0.5 USD per token.

The USD and share deltas are opposite to that of standalone splits, i.e USD delta is positive, and share delta is negative.

6.5. NegRisk Converts

The event structure is a bit tricky.

Convert Event Emissions
Fig. 2 — `convertPositions` flow. The right-hand table shows event offsets relative to the terminal `PositionsConverted` event (`i`) across four fee/collateral configurations. For the no-fee, v1 case used here: the trader's NO burn (`BatchTransfer`) falls between `i-3` and `i-6`; the YES mint to the trader is always at `i-1`.

There are 3 legs for this trade:

  1. Trader burns their NO tokens.
  2. Trader may or may not receive Collateral tokens.
  3. Trader receives YES tokens.

Because the PositionConverted event itself doesn’t log the correct token IDs that were converted, we use an adjacent BatchTransfer to determine the token IDs. For the YES minting leg, we use the BatchTransfer event that is right before the PositionConverted event, i.e if i is the PositionConverted event index, then i-1 is the YES BatchTransfer event index. For the NO burning leg, based on the figure above, the BatchTransfer is always between i-3 and i-6, if i is the index of the PositionConverted event. This event offset is fee-schedule-dependent. Fig above shows the offset shifts under the “Both Fee & Collateral” and “No Collateral” configurations. An interesting takeaway is that we can determine the type of convert, based on the difference between the indexes of the PositionConverted and BatchTransfer events.

The USD and share deltas are as follows:

  • USD delta is positive for the NO burning leg. Since for n NO positions burned, we receive n-1 NO positions worth USD, the USD value of each NO position is (n-1) * shares / n USD.
  • The shares delta is negative for the NO burning leg. The shares amount remains the same as shares.
  • The shares delta is positive for the YES minting leg, and the shares amount remains the same as shares.
  • The USD delta is 0 for the YES minting leg.

We already know that the USD received by the trader is equivalent to n-1 NO positions for n NO positions burned. Thus we can price the NO positions as (n-1) / n USD per token. The YES positions are priced as 0.

6.6. Standalone Transfers

Standalone transfers are transfers that do not involve any Polymarket contract. A SingleTransfer or BatchTransfer has 3 wallet fields: from, to, and operator. None of these fields must be a Polymarket contract address.

We also do not establish a monetary value for standalone transfers. This is once again a naive assumption, but this simplest way to price them. In the real world, these transfers could have economic value based on off-chain payments or an OTC agreement. The ideal way to price these transfers would be to use the price of the token at the time of transfer.

6.7. Summary of Pricing, Share Delta and USD Delta Logic

EventUSD logicShare LogicPricing LogicWhy
CLOB Trade$ -usd if buy, $ +usd if sell-shares if buy, +shares if sellusd / sharesCLOB trades involve USD trade for Shares
Split$ -usdshares$0.5$1 of collateral mints 1 YES + 1 NO — priced at parity
Merge$ +usd-shares$0.51 YES + 1 NO burns for $1 of collateral — same parity
Convert (NO burn leg)$ (n-1) x shares-shares(n − 1) × shares / nOf n NO tokens burned, n − 1 convert straight to USD
Convert (YES mint leg)$0shares$0The 1 remaining unit converts to YES ‘for free’ — the USD value already realized on the burn leg
Stray transfer$0-shares if sender, +shares if receiver$0No USD changes hands; pure share movement between wallets

6.8. Assembling it all

We union them all together to get the unified ledger. We can then compute the running share balance and different USD flows. Finally, we can compute the USD invested and USD realized. We can use the settlement_value from market details to compute the resolution profit. The Final PnL is USD on resolution plus USD realized minus USD invested.

In the SQL query, it looks like:

CLOB maker-taker fills +
Standalone splits +
Standalone merges +
Negative Risk conversions +
External ERC-1155 transfers +
Settlement and redemption

Unified event-level ledger

Running share balances +
Cumulative USD flows

Position settlement value

Resolution PnL

7. Validation

We choose a sample of the ledger to validate the outputs for correctness and sanity. We specifically choose all positions in markets that were created after October 1st, 2025, and ended with resolution or market end before April 28th, 2026. April 28th was chosen specifically to only factor in v1 version of the protocol.

MetricValuePercentage
Number of positions182.84m100%
Negative positions (final shares < 0)624.17k0.40%
Negative positions (final shares < -1)20.00%
Negative positions (final shares < -100)00.00%

Thus, we have achieved our first goal of non-negative shares.

How does our ledger compare to naive-only trade logic?

MetricOur LedgerNaive LedgerDelta
Number of positions182.84m15.99m🟢 +1043.46%
Ledger Traders1.61m916.72k🟢 +75.62%
Ledger Tokens1.49m218.87k🟢 +580.77%
Negative positions (final shares < 0)624.17k183.56k🟢 +295.96%
Negative positions (final shares < -1)284.97k🔻 -99.99%
Negative positions (final shares < -100)026.80k🔻 -100.00%

Our ledger improves coverage by a factor of 10x compared to the naive ledger, while reducing significant negative positions to zero in 6 months.

But how do we know it is correct and complete? To check for completeness, we compare with the ERC 1155 balance ledger, constructed from the running sum of deltas from all ERC 1155 transfers. The logic is that, since all positions must be ERC 1155 positions, the balance ledger should match the number of positions in our ledger.

MetricOur LedgerERC 1155Delta
Number of positions182.84m191.25m🔻 -4.39%
Ledger Traders1.61m1.61m⚪ 0.00%
Ledger Tokens1.49m1.51m🔻 -1.32%
Negative positions (final shares < 0)624.17k15.61m🔻 -96.00%
Negative positions (final shares < -1)20⚪ 0.00%
Negative positions (final shares < -100)00⚪ 0.00%

Our ledger approach closes with the ERC 1155 ledger.

To check for correctness, we need to compare the ledger against the Polymarket API. Since we have ~180 million positions, we sample 5000 positions at random to compare against the API. We bucket the absolute PnL into 6 buckets [0, 100, 1k, 10k, 100k, 1M]. We sample 1000 positions from each bucket. Then we also sample the lowest 1000 positions and the highest 1000 positions. We get exactly 7092 positions. We compare the sampled positions against the API and check for correctness. We define the correctness threshold as a difference greater than 10$ in PnL or 1% in PnL%.

When compared against the API,

  1. We get 5915 matches - i.e 1166 positions do not match with the API.
  2. 5834 positions fall under the threshold - 81 positions do not match with the API.
  3. Of the 81 positions, 14 positions are close to the threshold.

The remaining 67 positions have either a Transfer Out or a Negrisk NO Convert. Manual verification of these positions seems to suggest that the positions would match the API values if the NegRisk NO Conversions or Transfers were ignored.

Of the 1166 positions that did not match the API

  1. 138 positions belong to 0x05cd9922a5d37fae921fc5dee280a9dbc4c3b393 - The Auto Redemption contract
  2. 97 positions belong to 0xa5ef39c3d3e10d0b270233af41cac69796b12966 - The NegRisk Escrow contract
  3. 137 positions have absolute PnL less than 10$
  4. 789 positions have PnL less than -10$, all the way to -3,000,000$
  5. 17 positions have PnL above 10$, all the way to 300,000$

We check the top 2 most negative PnL and the top 2 most positive PnL positions to try and understand why the positions do not appear at the API at all. It doesn’t make much sense as to why they do not appear at the API at all.

  1. 0xae8758cf74d46eb0c9d889e50f1a089b1e3bc735 wallet NO position on Will Real Madrid CF win on 2026-03-07? - RC Celta de Vigo vs. Real Madrid CF market has 392 transfer events, but the API doesn’t have an entry
  2. 0x01542a212c9696da5b409cae879143b8966115a8 wallet YES on Will Bitcoin reach $105,000 in January? market has 405 transfer events, but the API has only logged the NO position, which itself has a $-307,989.28 PnL. In this case, the positive PnL of the YES position would register a net positive PnL.
TraderTokenPnLTokenIDCondition IDMarketDune Query
0xae8758cf74d46eb0c9d889e50f1a089b1e3bc735NO-3,255,428.68304601981044251922340114826277165264291189812891641939242633178262990555013030x832e608427a5d6db43947262d84a06bca5630a700eac7eed50e64d00b1a016dfWill Real Madrid CF win on 2026-03-07? - RC Celta de Vigo vs. Real Madrid CF🔗
0x742defec5672b86ae1366546977323d0b0fb33fbYES-3,222,068.65474947012466236839044670994392117120784967079979827814526436086906483474946910x22210ba50e974556bb194cd0f0176ae82dc2ac1b2db42e2ad2c20801ff9f5c8bWill Brentford FC win on 2026-03-16? - Brentford FC vs. Wolverhampton Wanderers FC🔗
0x01542a212c9696da5b409cae879143b8966115a8YES317,667.4858907537529060020525347854993056474516203122110197748305041682723225113757820x542fb07feb00feddb87c1d9e93ebf439a692c6b12e81e935ef2a5cda7de70dfbWill Bitcoin reach $105,000 in January?🔗
0x2eb5714ff6f20f5f9f7662c556dbef5e1c9bf4d4YES327,151.19736244328057801821509644439510458006669778111859630191339146189748585994582730x561ffbf7de21ef3781c441f30536b026d2b301d7a4a0145a8f526f98db049ba2Will Bitcoin reach $150,000 in March?🔗

8. Limitations

8.1. Pricing Approximations

A majority of the pricing limitations come from the fact that I didn’t spend time researching the optimal way to price the YES/NO tokens. This includes factoring in a lot of factors and features like:

  • Ask - Bid mid price
  • How to price wide spreads?
  • Last Execution price
  • How much time before an execution price is now considered stale?

Generalizing this pricing for a large volume of markets, many of which are illiquid, is a battle for another day.

8.1.1. Pricing Splits and Merges

When pricing Standalone splits and merges, we use a simple approximation of 0.5 USD per token. Our pricing affects the PnL of individual tokens, but converges when both YES and NO legs are settled. This effectively results in misattribution of PnL from one token to another. There are two ways to fix this:

  1. Use the price from the next leg of the Split or Merge, if any.
  2. Fall back to the current price of the YES/NO token.

8.1.2. Pricing Converts

In Converts, the NO tokens are priced correctly. However, the YES tokens are priced at $0 USD, which is a problem. The ideal way to price is to subtract the current worth of NO tokens that were burned and subtract the USD value of collateral paid out. Once against this depends on reliably pricing the NO tokens. This, however, might open another can of worms, where the cost basis of NO tokens is higher than the USD gained on Convert. This would denominate the YES token price as negative.

8.1.3. Pricing Transfers

Standalone transfers are priced at $0. A wallet thus receiving inherits a zero cost basis, which affects the eventual realized PnL.

8.2. Scope

The current ledger is tested against a subset of v1 data from October 2025 to April 2026. It’s safe to assume that the ledger is correct for this subset, but not necessarily for v2. This is primarily because I haven’t invested time into researching v2 changes. Specifically, the addition of fees to the protocol, without changes to the CTF logic, could mean potentially incorrect asset transfer info, post-fee reductions. In NegRisk Convert, we can already see this slightly play out, as the presence of fees affects the position of the events that are to be matched.

8.3. Flaw in Double Counting Filter

We exclude entire transactions from the Standalone Split/Merge detection if the said transaction contains an OrderFilled event. If a transaction batches a Standalone Split/Merge with a CLOB order with Split/Merge, the ledger drops these transactions. This is the reason why our ledger implementation has 2 negative balances.

Eg: Transaction

This should be easier to resolve with a more sophisticated CLOB trades table that can include the Split/Merge associated with a CLOB trade. Since DuneSQL already performs hash filtering via joins, the computational difference should be minimal with the CLOB trades table already built.

9. Queries

10. SQL Mapping to Ledger

SQL layerPurpose
short_list_marketsDefine the market universe and settlement information
trades_level_1Separate and prepare individual fills and full-order events
trades_level_2Classify and interpret trade structures
trades_level_3Reconstruct complementary/taker-side information
trades_level_4Produce normalized trade-level values
batch_transfersDecode ERC-1155 batch token movements
single_transfersDecode individual ERC-1155 transfers
splitsIdentify standalone position splits
mergesIdentify standalone position merges
converts_*Reconstruct Negative Risk conversion legs
trade_deltasConvert CLOB activity into share and USD deltas
merges_splits_convertsNormalize protocol operations
single_transfer_deltasNormalize external transfers
non_trade_txsCombine non-CLOB activity
audit_txsConstruct transaction-level validation data
audit_aggrAggregate validation results

11. References