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

# streamAccountUpdates

> Stream low-latency account confirmations, pending orders, active limits, and open trades.

`streamAccountUpdates()` emits full account snapshots for one or more traders. It combines subgraph polling, Alchemy contract-log overlays, and live price repricing so market opens and full closes can appear before subgraph indexing catches up.

```ts theme={null}
const client = await OstiumClient.createSelfAndSelf({
  traderPrivateKey: process.env.TRADER_PRIVATE_KEY as `0x${string}`,
  rpcUrl: process.env.ARB_RPC_URL!,
  alchemyApiKey: process.env.ALCHEMY_API_KEY!,
});

const stream = client.streamAccountUpdates();

stream.onUpdate(snapshot => {
  const account = snapshot[client.getTraderAddress().toLowerCase()];
  console.log(account.positions);
  console.log(account.orders);
  console.log(account.limits);
});

stream.onError(error => console.error(error));
```

Read-only clients and `OstiumSubgraphClient` require a `user` address array.

```ts theme={null}
const stream = client.streamAccountUpdates({
  user: ['0xTraderAddress'],
  alchemyApiKey: process.env.ALCHEMY_API_KEY!,
  pollIntervalMs: 3000,
});
```

## Multiple addresses

Pass one or more addresses in `user` to subscribe to accounts on a single stream. The stream uses one WebSocket, one poll loop, and one price feed regardless of how many addresses you watch — far cheaper than opening one stream per trader.

```ts theme={null}
const stream = client.streamAccountUpdates({
  user: ['0xTraderA', '0xTraderB', '0xTraderC'],
  alchemyApiKey: process.env.ALCHEMY_API_KEY!,
});

stream.onUpdate(snapshot => {
  console.log(snapshot['0xtradera'].positions);
  console.log(snapshot['0xtraderb'].orders);
});
```

The snapshot is keyed by normalized trader address. Each trader bucket contains `positions`, `orders`, and `limits`. Duplicate addresses are de-duplicated, and `stream.users` returns the subscribed addresses.

## Optimistic opens

For market opens, add an optimistic overlay immediately after submission and reconcile it once you know the on-chain order id.

```ts theme={null}
import { extractOrderIdFromReceipt } from '@ostium/builder-sdk';

const result = await client.openTrade(params);
const localId = stream.addOptimisticOpen(params, result);

const receipt = await publicClient.waitForTransactionReceipt({
  hash: result.txHash,
});
const orderId = extractOrderIdFromReceipt(receipt);

if (orderId) {
  stream.attachOrderId(localId, orderId, {
    initiatedTx: result.txHash,
  });
}
```

`addOptimisticOpen()` only supports market open orders. When streaming multiple addresses, pass the owning address as the third argument (`addOptimisticOpen(params, result, '0xTraderA')`); it is optional and defaults to the sole subscribed address when streaming one trader.

## Parameters

```ts theme={null}
interface StreamAccountUpdatesParams {
  /** One or more addresses to stream on one connection. */
  user?: `0x${string}`[];
  alchemyApiKey?: string;
  /** Reconciliation poll cadence. Defaults to 3000ms. */
  pollIntervalMs?: number;
}
```

`alchemyApiKey` can be provided when creating the client or when starting the stream. The stream throws if no Alchemy API key is available.

`pollIntervalMs` defaults to `3000`. Sub-second open/close confirmations come from the Alchemy event watchers (which also trigger an immediate poll), so the timer poll only paces reconciliation of changes with no watcher — limit fills, partial closes, TP/SL edits, and liquidations. Pass a lower value to restore a faster cadence. All subscribed traders are fetched in a single batched subgraph query per poll.

## Response schema

```ts theme={null}
interface OstiumAccountUpdatesStream {
  /** Trader addresses this stream is subscribed to, in the order supplied. */
  readonly users: `0x${string}`[];
  onUpdate(handler: (snapshot: AccountUpdatesSnapshot) => void): () => void;
  onError(handler: (error: Error) => void): this;
  getCurrent(): AccountUpdatesSnapshot;
  addOptimisticOpen(
    params: OpenTradeParams,
    submission?: SubmissionResult,
    user?: `0x${string}`,
  ): string;
  attachOrderId(
    localId: string,
    orderId: string,
    metadata?: { initiatedTx?: string; initiatedBlock?: string },
  ): void;
  close(): void;
}

interface AccountUpdatesForTrader {
  /** Open positions, formatted like getOpenPositions().pairPositions. */
  positions: PairPosition[];
  /** Pending market orders only. */
  orders: Order[];
  /** Active limit and stop orders. */
  limits: OpenOrder[];
}

type AccountUpdatesSnapshot = Record<string, AccountUpdatesForTrader>;
```

`positions`, `orders`, and `limits` use the same SDK-formatted objects returned by [getOpenPositions](/developer/reference/get-open-positions), [getOrders](/developer/reference/get-orders), and [getOpenOrders](/developer/reference/get-open-orders).
