> ## 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.

# createStore

> One subscribable object holding Ostium pairs, live prices, and a trader's positions — kept current for you.

`createStore(params?)` returns a single object holding pairs, live prices and the trader's positions, and keeps all three current — replacing the price socket, polls and recompute loop most integrations write by hand.

```ts theme={null}
const store = client.createStore();

const unsubscribe = store.subscribe(({ ready, positions, marginSummary }) => {
  if (!ready) return;
  render(positions, marginSummary);   // PnL and liquidation price already current
});

await client.openTrade({ /* … */ });
await store.refresh();                // pick the new position up at once

unsubscribe();
store.close();
```

Each input moves at its own cadence:

| Input     | Source               | Default cadence |
| --------- | -------------------- | --------------- |
| Prices    | WebSocket            | live            |
| Pairs     | `getPairs()`         | 60s             |
| Positions | `getOpenPositions()` | 15s             |

PnL, liquidation price and accrued rollover are recomputed locally on every tick, using the same [math helpers](/developer/reference/math-helpers) you could call yourself — so a price tick costs no network request.

## Parameters

| Parameter        | Type      | Default          | Description                                                                                                     |
| ---------------- | --------- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
| `user`           | `Address` | connected wallet | Trader to follow. Throws `INVALID_CONFIG` if there is none — a read-only client must pass one.                  |
| `pairPollMs`     | `number`  | `60000`          | How often to re-read pairs, for rollover accumulators.                                                          |
| `positionPollMs` | `number`  | `15000`          | How often to re-read positions, for membership changes.                                                         |
| `throttleMs`     | `number`  | `250`            | Minimum gap between emissions. Ticks arriving faster are coalesced, newest wins. Set `0` to emit on every tick. |

## Store interface

```ts theme={null}
interface OstiumStore {
  getState(): OstiumStoreState;
  subscribe(listener: (state: OstiumStoreState) => void): () => void;  // fires immediately, then on each update
  refresh(): Promise<void>;   // re-read pairs and positions now
  close(): void;              // stop polling and close the socket
}
```

## State schema

```ts theme={null}
interface OstiumStoreState {
  ready: boolean;                              // pairs, prices and positions have all arrived once
  positions: Array<Position & {
    rolloverAccrued: string;                   // accrued rollover in USDC
  }>;
  marginSummary: MarginSummary;                // same shape getOpenPositions() returns
  pairs: Record<string, Pair>;                 // empty until the first fetch lands
  pairMeta: Record<string, PairSnapshotEntry>; // available immediately — see below
  prices: Record<string, PriceData>;
  blockNumber?: string;                        // block the rollover figures are current as of
  lastError?: string;                          // last failed poll, if any
}
```

## Rendering before the first fetch

`pairMeta` is populated **synchronously** from a snapshot shipped with the package, so a market list renders on the first `getState()` instead of after a round trip. Live data replaces it once `getPairs()` resolves.

```ts theme={null}
const store = client.createStore();
selectMarkets(store.getState());   // already populated — no await
```

It carries pair names, category, leverage caps and minimum notional only — never prices, open interest or rollover data, which are always fetched live.

The same snapshot is exported as `PAIR_SNAPSHOT` if you want it without a store.

## Selectors

Plain functions of state, for the lookups every UI writes by hand:

```ts theme={null}
import {
  selectPosition, selectPositionsForPair, selectPrice, selectPair,
  selectPairMeta, selectMarkets, selectPositionsNearLiquidation, memoSelector,
} from '@ostium/builder-sdk';

const state = store.getState();

// pairId accepts the numeric string or a number — these are the same pair.
selectPosition(state, '0', 0);              // one position, by pair and index
selectPositionsForPair(state, '0');         // every position on a pair
selectPrice(state, '0');                    // latest tick
selectPair(state, '0');                     // live pair state
selectPairMeta(state, '0');                 // available before the first fetch
selectMarkets(state);                       // tradeable pairs, sorted by symbol
selectPositionsNearLiquidation(state, 10);  // within 10% of liquidation
```

## Using it in React

`subscribe` and `getState` already match the contract of React's `useSyncExternalStore`, so there is nothing to wire up and React is not an SDK dependency:

```tsx theme={null}
const selectBtc = memoSelector(s => selectPositionsForPair(s, '0'));

function BtcPositions() {
  const positions = useSyncExternalStore(store.subscribe, () => selectBtc(store.getState()));
  return <PositionList positions={positions} />;
}
```

`memoSelector` is required here: `useSyncExternalStore` needs `getSnapshot` to return a stable reference, and a selector that builds a fresh array on every call makes React loop.

It does not suppress re-renders on unrelated ticks — the store emits a new state whenever any watched price moves. To keep a component still while another pair moves, select a primitive, or compare by content first.

## Errors

A failed poll leaves a stale field, not a dead store: the store keeps running and reports the message on `lastError`.

## Exported types

`OstiumStore`, `OstiumStoreState` and `StorePosition` are the shapes above. `CreateStoreParams` is the parameter object. `PairSnapshotEntry` is one entry of `pairMeta`.

`StoreBackend` is the narrow interface the store reads through — `subgraph`, `streamPrices` and `getBlockNumber`. `client.createStore()` supplies it for you; it is exported so a test can hand the store a fake instead of a network.

## Related

* [Math helpers](/developer/reference/math-helpers) — the same numbers, without a store
* [streamPositionUpdates](/developer/reference/stream-position-updates) — re-price a payload you already have
* [streamAccountUpdates](/developer/reference/stream-account-updates) — low-latency confirmations
