Overview
Rendering a position looks like a solved problem. Take size, take price, multiply, show a number. Prediction markets break it in two places. First, a position can be finished without being closed: the event happened, the outcome is decided, and the money is still not yours. Second, losing positions go to exactly zero, which most portfolio UIs treat as a rendering bug and hide. Both produce the same support ticket: a user looking at a number that does not match what they can actually do. This guide covers modelling positions so that never happens.Read time: about 13 minutes. Front-end or product engineering context assumed.
TL;DR
- Positions arrive with cost basis already computed. Do not reconstruct it from fills.
- Unrealized PnL is a mark on what you hold. Realized PnL is a cashflow model, measuring USDC that actually moved.
- Positions have four states: open, awaiting resolution, resolved-unredeemed, redeemed.
- A losing position settles at $0 and that is correct, not a bug. It is also a realized loss that matters for tax.
- Use the Trade API for current holdings, the Data API for history and any public wallet.
- Numbers are JSON strings. Render with a decimal type.
What you will do
- Fetch positions with cost basis from the Trade API
- Distinguish realized from unrealized correctly, and explain the difference to users
- Implement the four-state model with the right action for each
- Handle losing positions without hiding them
- Pull historical views from the Data API for charts and closed positions
- Format probabilities so they read as both a price and a chance
What you will need
Knowledge- Comfort with a front-end framework, and a decimal library
- A Bravado API key with
trade.readfor the user’s own account - Nothing at all for public wallets, since Data API reads are open
Positions arrive with cost basis
average_entry_price is computed by a share-level FIFO engine replaying the wallet’s full history, not averaged from recent fills.
Realized and unrealized answer different questions
Bravado’s realized PnL is a cashflow model. It measures USDC that entered and left the wallet, net of fees, rather than inferring profit from price differences. That is why it reconciles against a bank-statement view of the account, and why the tax endpoints can be built on it.
For history across a wallet’s whole life rather than current holdings:
The four-state model
This is the core of the guide.Why two states are not enough
Collapseawaiting_resolution into “closed” and you tell the user they have money. They do not: the outcome is not final, and nothing can be done.
Collapse resolved_unredeemed into “open” and you hide the one action that actually matters, claiming the collateral. The user’s money sits unclaimed because your UI never surfaced a button.
Exact field names for the resolution flags depend on the response shape; confirm against the positions reference. The four-state distinction is what matters, not the field name.
Losing positions go to zero
A losing outcome settles at $0 per share. There is no partial recovery and nobody to sell to. Portfolio UIs frequently treat this as bad data and hide the row, or render it as an error. Both are wrong:- The loss is real and realized. Hiding it makes the portfolio total wrong.
- It is a disposition for tax purposes, and one that looks nothing like a trade. Tooling that only reads fills misses it entirely. See Generate a tax statement.
Historical views
The Trade API gives you current holdings. The Data API gives you everything else, and works on any public wallet:
Category exposure is the most under-used of these in product terms. It shows whether a trader is broadly capable or good at exactly one thing, which is genuinely interesting to a user looking at their own record and essential to anyone evaluating a leader to copy.
Formatting
0.62 is 62 cents and a 62% implied chance. Rendering 62¢ (62%) teaches the model without a tooltip.
Handle 503 as a state
Data API statement endpoints return503 with available: false while a wallet is still being computed. For a wallet nobody has queried before, this is expected.
Wrapping up
Two things separate a prediction market portfolio from a spot one: positions finish before they close, and losers go to exactly zero. Model the first with four states and the right action on each. Show the second plainly rather than hiding it. The rest is ordinary formatting work, provided you use a decimal type.Frequently asked questions
A user says a winning position shows money they cannot withdraw.
A user says a winning position shows money they cannot withdraw.
They are in
resolved_unredeemed. The outcome is final and the shares are worth a dollar each, but the collateral is not in the wallet until redeemed. Surface a redeem action rather than showing available cash.Why does my computed cost basis disagree with the API?
Why does my computed cost basis disagree with the API?
Almost certainly splits, merges, or redemptions being treated as ordinary trades. They are not. Use the
average_entry_price returned with the position.Should I hide positions worth zero?
Should I hide positions worth zero?
No. A losing outcome settling at zero is a real, realized loss, and it belongs in both the portfolio total and any tax export.
What is the difference between collateral_value and unrealized_pnl?
What is the difference between collateral_value and unrealized_pnl?
collateral_value is what the position is worth at the current mark. unrealized_pnl is that value minus what you paid. One is a level, the other is a change.Can I show another user's portfolio?
Can I show another user's portfolio?
Yes. Every Data API read works on any public address with no permission from the wallet owner and no funded account of your own.
Why is a 503 not an error?
Why is a 503 not an error?
PMWAS and statement endpoints compute a wallet’s full history on first request.
available: false means still working. Poll rather than failing.Resources
- Build a trading terminal, the surrounding UI
- Generate a tax statement, how dispositions are derived
- Positions endpoints, redeem, split, and merge
- Data API, historical and public-wallet views
- Numeric conventions, why everything is a string
- UMA resolution, what happens between concluded and final