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

# Read HIP-4 positions, balances, and trade history

> Reference for PredictionAccountAdapter: read positions, 30-day trade history, balances, and open orders for any wallet, plus subscribe to updates via polling.

The Account Adapter (`adapter.account`) gives you read access to a wallet's state on HIP-4 prediction markets. You can fetch open positions (held outcome tokens), the last 30 days of trade activity, raw spot balances including USDC, and currently resting orders. You can also subscribe to live position updates, which the adapter delivers by polling: the first result right away, then every 10 seconds. All methods accept a wallet `address` parameter, so you can query any account without authentication.

***

## `fetchPositions(address)`

Returns a wallet's open HIP-4 positions. Positions are derived from the spot clearinghouse state - each non-zero outcome token balance becomes a `PredictionPosition`. Midpoint prices and the event list are fetched in parallel to populate the prices and the event and market names.

```typescript theme={null}
const positions = await adapter.account.fetchPositions("0xYourAddress");
```

### Parameters

<ParamField path="address" type="string" required>
  The wallet address to query.
</ParamField>

### Return type

`Promise<PredictionPosition[]>`

<ResponseField name="marketId" type="string" required>
  The outcome ID extracted from the token coin string.
</ResponseField>

<ResponseField name="outcome" type="string" required>
  The balance coin as Hyperliquid reports it in spot balances (e.g. `"+5160"`). `parseSideCoin()` parses it.
</ResponseField>

<ResponseField name="outcomeName" type="string" required>
  Side name from `sideSpecs`, as Hyperliquid sends it (e.g. `"Yes"`, `"Hypurr"`, `"template:Yes"`).
</ResponseField>

<ResponseField name="parsedOutcomeName" type="string">
  Readable side name. Template sides are rendered from Hyperliquid's template registry (`"template:Yes"` reads `"Yes"`); other sides repeat `outcomeName`. Use this for display. Available from 1.3.0.
</ResponseField>

<ResponseField name="shares" type="string" required>
  Number of outcome tokens held, formatted to 6 decimal places.
</ResponseField>

<ResponseField name="avgCost" type="string" required>
  Average cost per token (`entryNtl / totalShares`), formatted to 6 decimal
  places.
</ResponseField>

<ResponseField name="currentPrice" type="string" required>
  The side's live midpoint price from `allMids`, or `"0"` when it has none. Spot balances name side coins `+<coin>` while mids use `#<coin>`, and the SDK maps one to the other. Releases before 1.3.0-beta.0 returned `"0"` for outcome positions.
</ResponseField>

<ResponseField name="unrealizedPnl" type="string" required>
  `(currentPrice - avgCost) * shares`, formatted to 6 decimal places.
</ResponseField>

<ResponseField name="potentialPayout" type="string" required>
  Maximum payout if the outcome resolves in your favor. Equal to `shares` (each
  token pays out 1 USDC).
</ResponseField>

<ResponseField name="eventStatus" type="string" required>
  Always `"active"` in the current implementation. No settlement status check is
  performed.
</ResponseField>

<ResponseField name="eventTitle" type="string" required>
  Title of the event the market belongs to, from `adapter.events.fetchEvents()`.
</ResponseField>

<ResponseField name="parsedEventTitle" type="string">
  Readable event title, from the event's `parsedTitle`. Available from 1.3.0.
</ResponseField>

<ResponseField name="marketQuestion" type="string" required>
  Question text of the market, from the same event list.
</ResponseField>

<ResponseField name="parsedMarketQuestion" type="string">
  Readable question text, from the market's `parsedQuestion`. Available from 1.3.0.
</ResponseField>

<Note>
  `fetchPositions` reads the first 200 events (`fetchEvents({ limit: 200 })`).
  For a position in a market outside those events, or if the event fetch fails,
  the event and market names are empty strings. Look the market up with
  `adapter.events.fetchEvent()` in that case.
</Note>

### Example

```typescript theme={null}
const positions = await adapter.account.fetchPositions("0xYourAddress");

for (const pos of positions) {
  console.log(`Outcome: ${pos.parsedOutcomeName ?? pos.outcomeName} (${pos.outcome})`);
  console.log(`  Shares:         ${pos.shares}`);
  console.log(`  Avg cost:       ${pos.avgCost}`);
  console.log(`  Current price:  ${pos.currentPrice}`);
  console.log(`  Unrealized PnL: ${pos.unrealizedPnl}`);
  console.log(`  Max payout:     ${pos.potentialPayout}`);
}
```

***

## `fetchActivity(address)`

Returns the wallet's trade fills on HIP-4 outcome coins from the last 30 days, newest first. Fills on other markets are filtered out. Hyperliquid returns at most 2,000 fills per request (before filtering), so a very active wallet may get less than 30 days of history.

```typescript theme={null}
const activity = await adapter.account.fetchActivity("0xYourAddress");
```

### Parameters

<ParamField path="address" type="string" required>
  The wallet address to query.
</ParamField>

### Return type

`Promise<PredictionActivity[]>`

<ResponseField name="id" type="string" required>
  Trade ID from the Hyperliquid `tid` field.
</ResponseField>

<ResponseField name="type" type="string" required>
  Always `"trade"` in the current implementation.
</ResponseField>

<ResponseField name="marketId" type="string">
  The outcome ID extracted from the fill's coin.
</ResponseField>

<ResponseField name="outcome" type="string">
  Raw coin string (e.g. `"#5160"`).
</ResponseField>

<ResponseField name="side" type="string">
  `"buy"` or `"sell"`.
</ResponseField>

<ResponseField name="price" type="string">
  Execution price.
</ResponseField>

<ResponseField name="size" type="string">
  Fill size.
</ResponseField>

<ResponseField name="amount" type="string">
  Never populated in the current implementation.
</ResponseField>

<ResponseField name="timestamp" type="number" required>
  Fill time in milliseconds.
</ResponseField>

### Example

```typescript theme={null}
const activity = await adapter.account.fetchActivity("0xYourAddress");

for (const entry of activity) {
  console.log(
    `${new Date(entry.timestamp).toISOString()} | ` +
      `${entry.side} ${entry.size} @ ${entry.price} | ` +
      `coin: ${entry.outcome}`,
  );
}
```

***

## `fetchBalance(address)`

Returns the raw spot clearinghouse balances for a wallet, including USDC and all outcome tokens. Use this when you need the full picture of what the wallet holds, not just open prediction positions.

```typescript theme={null}
const balances = await adapter.account.fetchBalance("0xYourAddress");
```

### Parameters

<ParamField path="address" type="string" required>
  The wallet address to query.
</ParamField>

### Return type

`Promise<Array<{ coin: string; total: string; hold: string }>>`: one entry per spot balance.

| Field | Type | Description |
| - | - | - |
| `coin` | `string` | Token coin string (e.g. `"USDC"`, `"+5160"`) |
| `total` | `string` | Total balance including held amount |
| `hold` | `string` | Amount currently on hold (in open orders) |

### Example

```typescript theme={null}
const balances = await adapter.account.fetchBalance("0xYourAddress");

for (const bal of balances) {
  console.log(`${bal.coin}: total=${bal.total} hold=${bal.hold}`);
  // "USDC: total=250.5 hold=0.0"
  // "+5160: total=100.0 hold=0.0"
}
```

***

## `fetchOpenOrders(address)`

Returns the currently resting (unfilled) orders for a wallet across all Hyperliquid markets, not only HIP-4. Use the `oid` field to build cancel requests.

```typescript theme={null}
const orders = await adapter.account.fetchOpenOrders("0xYourAddress");
```

### Parameters

<ParamField path="address" type="string" required>
  The wallet address to query.
</ParamField>

### Return type

A promise for an array of open orders. The SDK keeps these fields from Hyperliquid's `frontendOpenOrders` response:

| Field | Type | Description |
| - | - | - |
| `coin` | `string` | Coin string (e.g. `"#5160"`) |
| `side` | `"B"` \| `"A"` | `"B"` = buy, `"A"` = sell (ask) |
| `limitPx` | `string` | Limit price |
| `sz` | `string` | Remaining unfilled size |
| `oid` | `number` | Order ID - use this in `cancelOrder` |
| `timestamp` | `number` | Order creation time in milliseconds |

### Example

```typescript theme={null}
const orders = await adapter.account.fetchOpenOrders("0xYourAddress");

for (const order of orders) {
  console.log(
    `${order.coin} ${order.side === "B" ? "buy" : "sell"} ` +
      `${order.sz} @ ${order.limitPx} (oid: ${order.oid})`,
  );
}

// Cancel all open HIP-4 orders
import { parseSideCoin } from "@outcome.xyz/hip4";

const cancels = orders.flatMap((o) => {
  const parsed = parseSideCoin(o.coin); // null for non-HIP-4 coins
  return parsed
    ? [{ marketId: String(parsed.outcomeId), orderId: String(o.oid), outcome: o.coin }]
    : [];
});
if (cancels.length > 0) {
  await adapter.trading.cancelOrder(cancels);
}
```

***

## `subscribePositions(address, cb)`

Polls the wallet's positions and delivers each result to your callback. The first poll runs right away; each later poll starts 10 seconds after the previous one finishes. Each poll calls `fetchPositions`, which requests `spotClearinghouseState` and `allMids` and reads the event list (cached for 30 seconds). Errors during a poll are silently swallowed and the polling continues.

```typescript theme={null}
const unsubscribe = adapter.account.subscribePositions(
  "0xYourAddress",
  (positions) => {
    for (const pos of positions) {
      console.log(pos.parsedOutcomeName ?? pos.outcomeName, pos.shares, pos.unrealizedPnl);
    }
  },
);

// Stop polling when done
unsubscribe();
```

### Parameters

<ParamField path="address" type="string" required>
  The wallet address to poll.
</ParamField>

<ParamField path="cb" type="(positions: PredictionPosition[]) => void" required>
  Callback invoked after each successful poll. Receives the current
  `PredictionPosition[]`.
</ParamField>

### Return type

`Unsubscribe` - a `() => void` function. Call it to stop the polling loop.

<Note>
  The polling interval is fixed at 10 seconds. The first delivery arrives as
  soon as the first poll completes, so you don't need a separate
  `fetchPositions()` call before subscribing.
</Note>

### Example

```typescript theme={null}
// The first update arrives right away, then every 10 seconds
const unsubscribe = adapter.account.subscribePositions(
  "0xYourAddress",
  (positions) => {
    renderPositions(positions);
  },
);

// Clean up when the component unmounts or session ends
window.addEventListener("beforeunload", unsubscribe);
```


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