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

# Fetch HIP-4 order books, prices, trades, and candles

> Reference for PredictionMarketDataAdapter: fetch L2 order books, midpoint prices, recent trades, and OHLCV candles, or subscribe to live WebSocket streams.

The Market Data Adapter (`adapter.marketData`) gives you both snapshot and streaming access to HIP-4 market data. You can poll for order books, prices, trades, and OHLCV candles, or subscribe to live WebSocket feeds that deliver updates as they arrive. All market methods accept a `marketId`, which is the outcome ID as a string (e.g. `"516"`). `fetchOrderBook` and `fetchTrades` default to side 0 (the first side). `fetchCandles`, `subscribeOrderBook`, and `subscribeTrades` always use side 0.

<Note>
  `marketId` is always the **outcome ID as a string** - not the event ID and not the coin string. For example, if the coin is `"#5160"`, the market ID is `"516"`.
</Note>

***

## `fetchOrderBook(marketId, sideIndex?)`

Returns a full L2 order book snapshot for a given outcome side.

```typescript theme={null}
const book = await adapter.marketData.fetchOrderBook("516", 0);
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string (e.g. `"516"`).
</ParamField>

<ParamField path="sideIndex" type="number" default="0">
  Which side to fetch. `0` = first side (e.g. "Yes"), `1` = second side (e.g. "No"). Defaults to `0`.
</ParamField>

### Return type

`Promise<PredictionOrderBook>`

<ResponseField name="marketId" type="string" required>
  The outcome ID echoed back.
</ResponseField>

<ResponseField name="bids" type="PredictionOrderBookLevel[]" required>
  Buy-side price levels, each with `price` (string) and `size` (string).
</ResponseField>

<ResponseField name="asks" type="PredictionOrderBookLevel[]" required>
  Sell-side price levels, each with `price` (string) and `size` (string).
</ResponseField>

<ResponseField name="timestamp" type="number" required>
  Server-side timestamp of the snapshot.
</ResponseField>

### Example

```typescript theme={null}
const book = await adapter.marketData.fetchOrderBook("516", 0);

console.log("Top bid:", book.bids[0]?.price, "x", book.bids[0]?.size);
console.log("Top ask:", book.asks[0]?.price, "x", book.asks[0]?.size);
console.log("Snapshot at:", new Date(book.timestamp));
```

***

## `fetchPrice(marketId)`

Returns the current midpoint price for both sides of an outcome, with the real side names. Uses a 5-second cache backed by the `allMids` endpoint, so rapid successive calls avoid redundant network requests.

```typescript theme={null}
const price = await adapter.marketData.fetchPrice("516");
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

### Return type

`Promise<PredictionPrice>`

<ResponseField name="marketId" type="string" required>
  The outcome ID echoed back.
</ResponseField>

<ResponseField name="outcomes" type="array" required>
  One entry per side. Each entry contains:

  * `name` - the side name from the outcome's `sideSpecs` (e.g. `"Yes"`, `"No"`, `"Hypurr"`, `"template:Yes"`). The adapter loads side names before resolving them, so you only see the generic `"Side 0"` / `"Side 1"` for an outcome it doesn't know.
  * `parsedName` - the readable side name. Template sides are rendered from Hyperliquid's template registry (`"template:Yes"` reads `"Yes"`); other sides repeat `name`. Available from 1.3.0.
  * `price` - current midpoint as a decimal string (0 to 1). `"0"` when no mid is available.
  * `midpoint` - same value as `price` (both fields are set identically).
</ResponseField>

<ResponseField name="timestamp" type="number" required>
  Millisecond timestamp of the fetch.
</ResponseField>

### Example

```typescript theme={null}
const price = await adapter.marketData.fetchPrice("516");

for (const side of price.outcomes) {
  console.log(side.parsedName ?? side.name, side.midpoint);
  // "Yes" "0.62"
  // "No" "0.38"
}
```

***

## `fetchTrades(marketId, limit?, sideIndex?)`

Returns recent trades for one side of a market outcome.

```typescript theme={null}
const trades = await adapter.marketData.fetchTrades("516", 20);
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

<ParamField path="limit" type="number" default="50">
  Maximum number of trades to return.
</ParamField>

<ParamField path="sideIndex" type="number" default="0">
  Which side's trades to fetch. `0` = first side, `1` = second side.
</ParamField>

<Note>
  The `PredictionMarketDataAdapter` interface type declares only `marketId` and `limit`, so TypeScript rejects a third argument on `adapter.marketData`. The HIP-4 implementation (`HIP4MarketDataAdapter`) accepts `sideIndex`.
</Note>

### Return type

`Promise<PredictionTrade[]>`

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

<ResponseField name="marketId" type="string" required>
  The outcome ID.
</ResponseField>

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

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

<ResponseField name="price" type="string" required>
  Execution price as a decimal string.
</ResponseField>

<ResponseField name="size" type="string" required>
  Trade size.
</ResponseField>

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

### Example

```typescript theme={null}
const trades = await adapter.marketData.fetchTrades("516", 10);

for (const trade of trades) {
  console.log(`${trade.side} ${trade.size} @ ${trade.price} (${new Date(trade.timestamp).toISOString()})`);
  // "buy 5 @ 0.62 (2026-03-11T03:00:00.000Z)"
}
```

***

## `fetchCandles(marketId, interval?, startTime?, endTime?)`

Returns OHLCV candle data for side 0 of a market outcome.

```typescript theme={null}
const candles = await adapter.marketData.fetchCandles("516", "1h");
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

<ParamField path="interval" type="string" default="1h">
  Candle interval. Common values: `"1m"`, `"5m"`, `"15m"`, `"1h"`, `"4h"`, `"1d"`.
</ParamField>

<ParamField path="startTime" type="number">
  Start of the time range as a Unix millisecond timestamp. Defaults to 14 days before the current time.
</ParamField>

<ParamField path="endTime" type="number">
  End of the time range as a Unix millisecond timestamp. Defaults to the current time.
</ParamField>

### Return type

A promise for an array of candle objects. The SDK converts Hyperliquid's raw candles (`HLCandle`) to numbers:

| Field | Type | Description |
| - | - | - |
| `time` | `number` | Candle open time, in **seconds** |
| `open` | `number` | Open price |
| `high` | `number` | High price |
| `low` | `number` | Low price |
| `close` | `number` | Close price |
| `volume` | `number` | Volume |

### Example

```typescript theme={null}
const now = Date.now();
const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;

const candles = await adapter.marketData.fetchCandles("516", "1h", oneWeekAgo, now);

for (const candle of candles) {
  console.log(`${new Date(candle.time * 1000).toISOString()} O:${candle.open} H:${candle.high} L:${candle.low} C:${candle.close}`);
}
```

***

## `subscribeOrderBook(marketId, cb)`

Opens a real-time WebSocket subscription to the L2 order book for side 0 of a market outcome. The callback receives a full book snapshot on each update.

```typescript theme={null}
const unsubscribe = adapter.marketData.subscribeOrderBook("516", (book) => {
  console.log("bids:", book.bids.length, "asks:", book.asks.length);
});

// Later, when you no longer need updates:
unsubscribe();
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

<ParamField path="cb" type="(book: PredictionOrderBook) => void" required>
  Callback invoked on each book update. Receives a `PredictionOrderBook` snapshot.
</ParamField>

### Return type

`Unsubscribe` - a `() => void` function. Call it to stop receiving updates and release the WebSocket subscription.

<Tip>
  The adapter shares a single WebSocket connection across all subscriptions. The connection opens lazily on the first subscription and closes automatically when the last subscriber unsubscribes.
</Tip>

***

## `subscribePrice(marketId, cb)`

Opens a real-time WebSocket subscription to midpoint prices for both sides of a market outcome. The callback fires on every `allMids` frame that includes a mid for either side. Side names resolve once the adapter has loaded them (call `initialize()` first); until then they read `"Side 0"` and `"Side 1"`. Each side carries `name` and `parsedName`, as in `fetchPrice`.

```typescript theme={null}
const unsubscribe = adapter.marketData.subscribePrice("516", (price) => {
  for (const side of price.outcomes) {
    console.log(side.name, side.midpoint);
  }
});

// Stop receiving updates:
unsubscribe();
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

<ParamField path="cb" type="(price: PredictionPrice) => void" required>
  Callback invoked whenever an `allMids` frame includes a mid for either side of this outcome.
</ParamField>

### Return type

`Unsubscribe`

***

## `subscribeTrades(marketId, cb)`

Opens a real-time WebSocket subscription to the trade stream for side 0 of a market outcome. Each incoming trade is dispatched individually to your callback.

```typescript theme={null}
const unsubscribe = adapter.marketData.subscribeTrades("516", (trade) => {
  console.log(`${trade.side} ${trade.size} @ ${trade.price}`);
});

// Stop receiving updates:
unsubscribe();
```

### Parameters

<ParamField path="marketId" type="string" required>
  The outcome ID as a string.
</ParamField>

<ParamField path="cb" type="(trade: PredictionTrade) => void" required>
  Callback invoked for each individual trade.
</ParamField>

### Return type

`Unsubscribe`

***

## Managing multiple subscriptions

You can hold multiple `Unsubscribe` functions and clean them all up together:

```typescript theme={null}
const subs = [
  adapter.marketData.subscribeOrderBook("516", onBook),
  adapter.marketData.subscribePrice("516", onPrice),
  adapter.marketData.subscribeTrades("516", onTrade),
];

// Clean up all subscriptions at once
function cleanup() {
  subs.forEach((unsub) => unsub());
}
```

<Warning>
  Data received during a WebSocket disconnect is lost, even though the connection auto-reconnects with exponential backoff (up to 10 attempts). Design your application to tolerate brief gaps in the real-time stream.
</Warning>


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