Skip to main content

Overview

Tax reporting for prediction markets is harder than for spot trading, and not for the reason people expect. The difficulty is not volume. It is that a position can end three different ways, and only one of them looks like a trade. You can sell shares on the book. You can redeem winning shares after resolution. Or your shares can expire worthless, which produces a realized loss with no transaction anywhere that resembles a sale. Tooling built by reading a fill history catches the first case, sometimes catches the second, and silently misses the third. That understates losses, which is the direction nobody discovers by accident. This guide pulls dispositions that account for all three, reconciles them, and exports something defensible.
This covers pulling and checking data. It is not tax advice. Rules vary by jurisdiction and by how a taxpayer’s activity is characterised. A qualified preparer should make those calls.
Read time: about 13 minutes. No prior tax engineering experience assumed.

TL;DR

  • GET /traders/{address}/tax-report gives per-year totals. GET /traders/{address}/statements/r1 gives the line items.
  • Rows are computed from on-chain settlement records replayed through a share-level FIFO cost-basis engine, not estimated from fills.
  • Paginate. Missing the tail silently understates gains.
  • Reconcile with GET /traders/{address}/reconciliation, the R8 certificate.
  • Do not reconstruct cost basis yourself. Splits, merges, and redemptions are not buys and sells.
  • 503 with available: false means still computing, not failed.

What you will do

  • Pull a per-year tax summary for a wallet
  • Pull the underlying Form-8949-style disposition rows, with pagination
  • Run two independent reconciliation checks before trusting the numbers
  • Understand the three ways a position ends and why the third is easy to miss
  • Handle the processing state for wallets with long histories
  • Export to CSV without reintroducing rounding error

What you will need

Knowledge
  • Comfort with paginated APIs and CSV export
  • No tax expertise required to follow the mechanics
Tools and access
  • A Bravado API key. Data API reads work on any public wallet, so you can run this against an address you do not control.
  • Python with requests

Start with the summary

Per-calendar-year totals: short-term and long-term capital gains, income by kind, and capital flows. This is the number that ends up on a return. Everything below exists to support it.

Then the line items

Each row is a closed lot: acquisition date, disposition date, proceeds, cost basis, and the resulting gain or loss. That is the Form 8949 shape, which is why a preparer can work from it directly.
Use the standalone /statements/r1 endpoint rather than the full /statements response for active wallets. The complete §20 statement carries all eight reports (R1 through R8) and gets large quickly. R1 is the only one most tax workflows need.

Paginate properly

Disposition sets run long, and a truncated pull is worse than no pull because it looks complete.
Raising limit is not a substitute for following the cursor. Follow next_cursor until it is absent, or you will confidently report a partial year.

Reconcile before you file

This is what makes the output defensible rather than merely available.

The R8 certificate

R8 verifies the statement is internally consistent: that the reports agree with each other and with the underlying settlement data. It exists precisely so a third party does not have to take the numbers on trust.

Two checks of your own

1

Line items should sum to the summary

Total gain_loss across R1 rows should match the capital gains in the tax report. A mismatch almost always means a missed page or a year filter that differs between the two calls.
2

Every disposition should trace to an event

Cross-check against GET /traders/{address}/trades for the period. Every disposition should correspond to a sale, a redemption, or an expiry. Anything that traces to nothing is worth asking about before filing.
Expected output:
If match is False, stop and find out why before anything downstream uses these numbers.

The three ways a position ends

That third row is the one that matters most, and the one home-grown tooling misses. Nothing about it looks like a trade. There is no counterparty, no fill, no transaction to find in a trade log. The shares simply stop being worth anything when the market resolves against you. It is still a realized loss, and one that is often material, because losing outcomes are the most common way a prediction market position ends.
Do not reconstruct cost basis from the trade log. Splits mint a complete outcome set from collateral; merges destroy one; redemptions convert shares to cash at a fixed price. Treating any of those as ordinary buys and sells produces a basis that disagrees with the chain, and the error compounds with account age. The R1 rows already account for all of it.

Handle the processing state

Tax and PMWAS endpoints compute a wallet’s full history on first request. For a long-lived wallet that takes a while.
A 503 with available: false is a state, not an error. Surface it as “preparing your statement” rather than a failure, and poll on a sane interval.

Export

Keep values as strings on the way out. Converting to float for a CSV reintroduces exactly the rounding error the FIFO engine exists to avoid, and a preparer reconciling your export against the API will find the discrepancy before you do.

Wrapping up

Two calls give you the numbers, one gives you the proof, and two checks of your own make it defensible. The conceptual point worth carrying: a prediction market position ends three ways, and the most common one leaves no trace in a trade log. Any tax tool built on fills alone will understate losses. That is the reason to use derived dispositions rather than rolling your own.

Frequently asked questions

Because splits, merges, and redemptions are not buys and sells. A split mints a complete outcome set from collateral, a merge destroys one, and a redemption converts shares at a fixed price. Treating them as trades produces a basis that drifts further from reality the longer the account has been active.
Almost always a missed page or a mismatched year filter. Confirm you followed next_cursor to the end and that both calls used the same year.
It verifies the statement is internally consistent, so a preparer or auditor can check the numbers rather than trusting them. Useful whenever someone other than you has to accept the output.
Because it is one. The shares settled at $0 and the loss is realized, even though no sale occurred. Omitting it understates losses.
No. available: false means the wallet’s history is still being computed, which happens on first request for wallets with long histories. Poll rather than failing.
Yes. Every Data API read works on any public address, with no permission from the owner and no funded account of your own.

Resources