> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metastreams.io/llms.txt
> Use this file to discover all available pages before exploring further.

# spot.positions

> Streams a wallet's full position in a token each time a trade or transfer changes it.

Streams a wallet's full position in a token each time a trade or transfer changes it.

Subscribe on an open stream connection. See [WebSocket streams](https://docs.metastreams.io/streams/overview.md) for connections, frames and reconnection.

## Filters

**chain** `string` *required*

[Chain slug](https://docs.metastreams.io/concepts/chains.md) of the positions.

**wallets** `string[]` *required*

One or more wallet addresses on `chain`. For a single wallet, `wallet` with one address is also accepted. Each wallet uses one unit of the [address limit](https://docs.metastreams.io/streams/filters.md#address-limits).

## Message format

Each update's `data` is a wallet's full position in one token after a change, in the same row shape that [Positions](https://docs.metastreams.io/reference/rest/positions.md) returns. Keep one row per `chain`, `walletAddress` and token, choosing the row with the highest `version`.

Streamed positions do not include prices. `value`, `unrealizedPnl`, `totalPnl` and `pnlPct` are `null`, as is every `token` field except `chain` and `address`. To price a row, follow its token on [`spot.tokens`](https://docs.metastreams.io/reference/streams/spot-tokens) and apply the same formulas as the REST endpoint:

* `value` is `holdings` times the token's price.
* `unrealizedPnl` is `value` minus `holdings` times `avgCost`.
* `totalPnl` is `realizedPnl` plus `unrealizedPnl`.
* `pnlPct` is `totalPnl.usd` divided by `invested.usd`, times 100. It stays `null` when `invested.usd` is zero.

## Initial snapshot

1. Subscribe.
2. Read every page of [Positions](https://docs.metastreams.io/reference/rest/positions.md) with the same wallets, `chains` set to the subscription's chain, and `hideLowLiquidity=false`. The stream does not filter anything out, so the REST request must not either. The request returns only open positions; to keep closed rows, also request `lifecycle=closed`.
3. Merge the REST response and the updates on `version`.

Repeat the REST request after every reconnect.

## Errors

| Code | When |
| - | - |
| `VALIDATION_ERROR` | Missing `chain`; no wallet; an address in the wrong format for the chain; an unknown filter field; an exceeded [address limit](https://docs.metastreams.io/streams/filters.md#address-limits) |
| `UNSUPPORTED_CHAIN` | Unsupported `chain` slug |

```json Subscribe theme={null}
{
  "action": "subscribe",
  "channel": "spot.positions",
  "filters": {
    "chain": "solana",
    "wallets": ["7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5"]
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.