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.Read time: about 13 minutes. No prior tax engineering experience assumed.
TL;DR
GET /traders/{address}/tax-reportgives per-year totals.GET /traders/{address}/statements/r1gives 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.
503withavailable: falsemeans 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
- 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
Then the line items
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.Reconcile before you file
This is what makes the output defensible rather than merely available.The R8 certificate
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.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.
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.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
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
Why not compute cost basis from the trade history myself?
Why not compute cost basis from the trade history myself?
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.
My line items do not sum to the summary.
My line items do not sum to the summary.
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.What is the R8 certificate for?
What is the R8 certificate for?
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.
Why does a losing position appear as a disposition?
Why does a losing position appear as a disposition?
Because it is one. The shares settled at $0 and the loss is realized, even though no sale occurred. Omitting it understates losses.
The endpoint returned 503. Is something broken?
The endpoint returned 503. Is something broken?
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.Can I run this for a wallet I do not control?
Can I run this for a wallet I do not control?
Yes. Every Data API read works on any public address, with no permission from the owner and no funded account of your own.
Resources
- Tax report endpoint, per-year summary reference
- R1 dispositions, line item reference
- R8 reconciliation, certificate reference
- Data API, the accounting model behind these numbers
- Display positions and PnL, realized versus unrealized
- Numeric conventions, why values are strings
- Pagination, cursor behaviour on large statements