# Portfolio

> Reconcile AGARA collateral, positions, open orders, fills, activities, and withdrawals.

Portfolio reads require a personal access token with `portfolio:read`. These
docs cover the `AGARA` exchange; keep the returned `exchange` fields in your
models so each row remains self-identifying.

## Summary [#summary]

`GET /trade/v1/portfolio/summary`

Use the summary before sizing a new order and after any deposit, withdrawal,
position operation, or automatic redemption.

```bash
curl "$AGARA_BASE_URL/trade/v1/portfolio/summary?exchanges=AGARA" \
  -H "Authorization: Bearer $AGARA_API_TOKEN"
```

```json
{
  "summaries": [
    {
      "exchange": "AGARA",
      "cash_balance_micro": "250000000",
      "free_cash_micro": "250000000",
      "positions_value_micro": "18500000",
      "portfolio_value_micro": "268500000",
      "open_cost_basis_micro": "17200000",
      "open_unrealized_pnl_micro": "1300000",
      "accounting_status": "AVAILABLE",
      "as_of": "2026-07-20T12:00:00.000Z"
    }
  ]
}
```

| Field                       | Meaning                                     |
| --------------------------- | ------------------------------------------- |
| `cash_balance_micro`        | Current collateral balance                  |
| `free_cash_micro`           | Cash available to AGARA's BUY validation    |
| `positions_value_micro`     | Marked value of open positions              |
| `portfolio_value_micro`     | Cash plus marked positions                  |
| `open_cost_basis_micro`     | Cost basis still attached to open positions |
| `open_unrealized_pnl_micro` | Marked value less open cost basis           |
| `as_of`                     | Timestamp of this exchange snapshot         |

Nonterminal BUY commitments are not a separate summary field. Fully page
[`POST /trade/v1/orders/list`](/docs/orders#list-your-orders) and account for
their remaining collateral when your strategy computes its own available
quoting budget. Use [open orders](#open-orders) for the portfolio's resting and
active subset.

## Positions [#positions]

`POST /trade/v1/portfolio/positions/list`

Positions are returned in one unpaginated response. Treat the AGARA subset as
complete only when `unavailable_exchanges` does not include `AGARA`; require the
array to be empty for a complete multi-exchange response.

```json
{
  "condition_ids": [],
  "exchanges": ["AGARA"]
}
```

An empty `condition_ids` array returns every current position. Use one or more
condition IDs to narrow the snapshot.

Positions in the most recently registered markets appear first. When market
registration times match, positions are ordered by current value, highest first,
then by exchange name alphabetically. Positions with unavailable market
registration times appear last.

```json
{
  "positions": [
    {
      "exchange": "AGARA",
      "condition_id": "0x...",
      "token_id": "1234567890",
      "shares_micro": "5000000",
      "available_shares_micro": "3000000",
      "avg_price_micro": "430000",
      "open_cost_basis_micro": "2150000",
      "accounting_status": "AVAILABLE",
      "current_price_micro": "470000",
      "current_value_micro": "2350000",
      "to_win_micro": "5000000",
      "profit_loss_micro": "200000",
      "profit_loss_percent": "9.302325581395348837",
      "redeemable": false,
      "mergeable": false,
      "mergeable_shares_micro": null
    }
  ],
  "markets": {
    "1234567890": {
      "market_id": "1f9e566d-7823-49fb-a5c5-5a193eba3209",
      "market_title": "Will City A win?",
      "outcome_name": "Yes",
      "logo_url": null,
      "event_slug": "city-a-vs-city-b",
      "state": "ACTIVE",
      "neg_risk_id": null,
      "display": { "market": {}, "outcome": {} }
    }
  },
  "events": {
    "city-a-vs-city-b": {
      "event_title": "City A vs City B",
      "display": { "event": {} }
    }
  },
  "unavailable_exchanges": [],
  "as_of": "2026-07-20T12:00:00.000Z"
}
```

Average entry price uses the moving weighted-average acquisition cost of held
shares, excluding trading fees. Selling removes proportional cost; closing a
position clears its cost, so a later purchase starts a new average. Splitting a
complete set assigns 50¢ per share to each outcome.

Holdings update immediately. The average and cost can briefly reflect earlier
activity. `open_cost_basis_micro` applies the latest available average to the
returned held quantity; use that field when calculating P\&L instead of multiplying
by the rounded `avg_price_micro`. `profit_loss_percent` is a percentage string:
`"9.3"` means 9.3%.

Keep displaying shares when `accounting_status` is `PENDING`, and display an
updating indicator for `null` average, cost or P\&L fields. Retry your normal
portfolio read to receive updated accounting. If the status is `UNAVAILABLE`,
display those amounts as unavailable rather than zero. `AVAILABLE` means an
average is available; it does not guarantee that it includes the latest fill.

Quoted current value remains available even while cost is pending. When neither
a quote nor an acquisition average is available, current price and value are
also `null`. Summary cost and P\&L are `null` if any held position lacks cost;
summary position and portfolio values are `null` only if a holding cannot be
valued. Cash remains available, and an empty portfolio has zero position totals.

`shares_micro` is your total holding. Use `available_shares_micro` as the maximum
size for a new sell order — it excludes shares your own open sell orders have
already committed, so `"0"` means every share you hold is already up for sale.

`mergeable` and `mergeable_shares_micro` are advisory snapshots for a held,
available complete pair. `redeemable` identifies a resolved position; supported
standalone markets are handled by [automatic redemption](/docs/automatic-redemption).
The sidecar's `state` tells you where each position's market is in its lifecycle:
once it reaches `PROPOSED` a winner or a split has been proposed, so the
position can no longer be sold and its payout is on the way. Positions in a
market with a `neg_risk_id` are the exception: those are not redeemed
automatically.
Retry when `unavailable_exchanges` contains `AGARA` because that snapshot is
missing AGARA rows.

`to_win_micro` is what the position pays if its outcome wins: one collateral
unit per share. When a market's proposal or resolution is a
[split](/docs/automatic-redemption#split-resolutions), it is your shares times
that outcome's split price, rounded down: 10 YES shares on a split that pays
`37¢` per YES share show `"3700000"`.

## Open orders [#open-orders]

`POST /trade/v1/portfolio/open-orders/list`

```json
{
  "token_ids": [],
  "exchanges": ["AGARA"],
  "limit": 100
}
```

The response contains:

* `orders`: the active subset used by the portfolio view, in the same shape
  documented under [Orders](/docs/orders#look-up-one-order);
* `markets`: metadata keyed by `token_id`, including each market's `state`,
  `neg_risk_id`, and raw market/outcome `display` objects;
* `events`: metadata keyed by event slug, including the raw event `display`;
* `pagination`: `next_cursor` and the applied `limit`;
* `as_of`: the snapshot time.

Orders are newest first. Pass `pagination.next_cursor` back as `cursor` with
the same `token_ids` and `exchanges` filters. A null cursor means the walk is
complete.

This is an active-order view, not lifecycle history. For cancellation recovery
or an audit of how each order ended, fully page
[`POST /trade/v1/orders/list`](/docs/orders#list-your-orders) and follow each
tracked order to a terminal state.

## Trades [#trades]

`GET /trade/v1/portfolio/trades`

Query parameters are `limit` (default and maximum `500`) and an opaque
`cursor` from the previous response.

```json
{
  "trades": [
    {
      "exchange": "AGARA",
      "trade_id": "<uuid>",
      "fill_id": "42",
      "order_id": "<uuid>",
      "token_id": "1234567890",
      "side": "BUY",
      "shares_micro": "1000000",
      "price_micro": "470000",
      "fee_micro": "0",
      "role": "MAKER",
      "status": "CONFIRMED",
      "transaction_hash": "0x...",
      "executed_at": "2026-07-20T11:58:00.000Z"
    }
  ],
  "markets": {},
  "events": {},
  "pagination": { "next_cursor": null, "limit": 500 },
  "unavailable_exchanges": [],
  "as_of": "2026-07-20T12:00:00.000Z"
}
```

`fill_id` is serialized as a string. `transaction_hash` remains null until
settlement supplies one. Any page can be partial, so check
`unavailable_exchanges` after every response. If an AGARA-only page contains
`AGARA`, discard the entire accumulated walk and restart without a cursor. Do
the same for a multi-exchange walk whenever the array is nonempty. Continue
only after every page satisfies the condition for your requested exchange set,
and stop when `next_cursor` is null.

## Activities [#activities]

`GET /trade/v1/portfolio/activities`

Activities show AGARA history sorted newest first. `limit`
defaults to `50` and accepts `1` through `500`; pass the opaque `cursor` to
continue to older activities. Each page returns current data; `as_of` is the time
that page was read. Refresh from page one to see newer activities.

The `type` discriminator selects one of these wire shapes:

| Type         | Fields in addition to `type`, `exchange`, `id`, and `created_at`                                                                                                        |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ORDER`      | `order_id`, current `status`, `fill_status`, `condition_id`, `token_id`, `side`, `filled_shares_micro`, `average_fill_price_micro`, `filled_amount_micro`, `fees_micro` |
| `SPLIT`      | `condition_id`, `shares_micro`, `amount_micro`, `tx_hash`                                                                                                               |
| `MERGE`      | `condition_id`, `shares_micro`, `amount_micro`, `tx_hash`                                                                                                               |
| `REDEEM`     | `condition_id`, redeemed `token_id`, `shares_micro`, `amount_micro`, `tx_hash`                                                                                          |
| `DEPOSIT`    | `amount_micro`, `chain_id`, `token_address`, `counterparty_address`, `tx_hash`                                                                                          |
| `WITHDRAWAL` | `amount_micro`, `chain_id`, `token_address`, `counterparty_address`, `tx_hash`                                                                                          |

An `ORDER` appears after its first recorded fill, including while the remaining
shares are still open. One row collects all fills for that order. Later fills
update its totals and current `status`; `created_at` stays at the first fill.
Unfilled orders do not appear here. `fill_status` is `FULL` or `PARTIAL`.

A `REDEEM` appears separately for each outcome you held. `shares_micro` is
positive for both outcomes. `amount_micro` is the collateral received: the
shares times the price their outcome paid, rounded down. For winners, that is
one unit per share. For losers, it is zero: 7 losing shares give
`shares_micro: "7000000"` and `amount_micro: "0"`. On a
[split resolution](/docs/automatic-redemption#split-resolutions), each outcome
pays its own price, so 7 NO shares paid at `63¢` give `amount_micro: "4410000"`.

Confirmed LP rewards appear as `LP_PAYOUT`, with `amount_micro`, `epoch_date`, and
`tx_hash`. The same reward transfer does not also appear as a deposit.

If a cursor returns `422`, restart from page one.

```json
{
  "activities": [
    {
      "type": "REDEEM",
      "exchange": "AGARA",
      "id": "redeem:<uuid>",
      "condition_id": "0x...",
      "token_id": "1234567890",
      "shares_micro": "5000000",
      "amount_micro": "5000000",
      "tx_hash": "0x...",
      "created_at": "2026-07-20T11:55:00.000Z"
    }
  ],
  "markets": {},
  "conditions": {},
  "events": {},
  "pagination": { "next_cursor": null, "limit": 50 },
  "as_of": "2026-07-20T12:00:00.000Z"
}
```

The metadata sidecars cover only rows present on the current page:
`markets` is keyed by outcome token ID, `conditions` by condition ID, and
`events` by event slug. Activities do not return `unavailable_exchanges`.

## Position operations [#position-operations]

Split and merge convert between collateral and complete binary token pairs:

* `POST /trade/v1/portfolio/positions/split` requires `positions:split`;
* `POST /trade/v1/portfolio/positions/merge` requires `positions:merge`.

Both accept a scoped personal access token and return `201` with
`batch_hash`, `status: "PENDING"`, and `as_of`. Poll
`GET /trade/v1/batches/{batch_hash}` for settlement. If it finishes as
`FAILED` or `FAILED_DIVERGENT`, inspect the typed `failure` object and wait for
`unwound_at` before treating provisional inventory as restored. If a lower
batch is `FAILED_DIVERGENT`, a higher failed batch's `unwound_at` alone is not
proof of restoration; keep mutations paused and follow the
[stranded-sequence recovery
rule](/docs/api-traders/account-batches#what-you-observe-on-failure). See
[Positions](/docs/on-chain/positions) for request shapes, validation, and
cross-match settlement.

Supported resolved standalone markets use
[automatic redemption](/docs/automatic-redemption); no client-submitted operation
is required.

## Withdrawals [#withdrawals]

The standard withdrawal flow requires a current browser session. A personal
access token alone cannot execute it.

* Same-chain withdrawal sends `USDC` to an address on
  Base.
* Send to a wallet address, not the token's own address. A request that names
  the token as its recipient is refused with a `422` error, because the token's
  own address is not a wallet.
* Accepted AGARA withdrawals return `201` with a pending batch hash. Poll the
  batch until it reaches `SETTLED` before treating destination funds as final;
  a terminal failure carries the same typed `failure` object.
* Requests queue per account and settle in order. A `409` means the queue is
  full; retry once a queued request finishes.
* The minimum is `10000` micro units. The environment's batch ceiling can
  impose a tighter bound.

For a cross-chain withdrawal:

1. Use a personal access token to read
   `GET /trade/v1/portfolio/bridge/withdraw/supported-assets`.
2. Request `POST /trade/v1/portfolio/bridge/withdraw/quote` with the
   destination chain, token, recipient, and source amount.
3. Review the current quote, then confirm in the signed-in web flow.

If a route refunds to the AGARA account, the return appears as a `DEPOSIT`
activity. A quoted route minimum can be higher than the standard withdrawal
minimum. Request a new quote before retrying.

**Withdrawing to Solana.** When Solana is offered, its supported asset has
`address_type` `SOL` and a base58 token address. Send both values back exactly
as listed.

* Set `recipient_address` to the base58 address of a Solana wallet you
  control, exactly as the wallet shows it. Solana addresses are case-sensitive,
  so never change their letter case.
* Some addresses are refused with a `422` error before anything moves: a
  wallet's associated token account, a program-controlled wallet such as a
  multisig vault, and the USDC token address itself. Any other address is
  accepted exactly as entered.
* The wallet must already have a USDC account; an empty one is fine. If it
  does not, the quote and the withdrawal answer
  `422 recipient_token_account_missing` and nothing moves. Receiving any amount
  of USDC in that wallet once creates the account; then request a new quote.
* `SETTLED` means the USDC has left your AGARA account. It usually reaches the
  Solana wallet within a minute. A withdrawal to Solana cannot be reversed or
  refunded, and USDC sent to an address that no wallet controls is lost, so
  check the address before you confirm.

## Common pattern [#common-pattern]

Before placing a new quote, fetch summary and fully page the order list. Use the
summary's `free_cash_micro` as AGARA's current BUY-validation cash, then apply
your own risk limits and account for every remaining nonterminal commitment.
Use the portfolio open-orders endpoint for the resting-liquidity view.

Every portfolio endpoint's full request and response shape is in the
[Portfolio API reference](/docs/api-reference/portfolio).
