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

# Market Data Hooks

> Trading pairs, prices, candles, order books, depth and top of book, public trades and liquidations, and fees.

## useMarket

```tsx theme={null}
import { useMarket } from "@0xmonaco/react";

const {
  getPaginatedTradingPairs,
  getTradingPairBySymbol,
  getCandlesticks,
  getMarketMetadata,
  // Perp market helpers
  getPerpMarketConfig,
  getPerpMarketSummary,
  getMarkPrice,
  getIndexPrice,
  getFundingState,
  listFundingHistory,
  getOpenInterest,
  getTraderMetrics,
  getTradeFlow,
  listLiquidationEvents,
  getLiquidationMetrics,
} = useMarket();
```

### Trading Pairs

**getPaginatedTradingPairs** `(params?) =\> Promise\<GetTradingPairsResponse\>`

Fetch paginated list of trading pairs.

**Returns:** `{ tradingPairs: TradingPair[], page, pageSize, total, totalPages }`

**getTradingPairBySymbol** `(symbol: string) =\> Promise\<TradingPair | undefined\>`

Get a specific trading pair by symbol. Resolves `undefined` on a miss (never throws), so guard the result before dereferencing. The hook forwards only `symbol`; to disambiguate a symbol listed in both SPOT and MARGIN, call `sdk.market.getTradingPairBySymbol(symbol, marketType)` on the core SDK directly.

### Market Data

**getCandlesticks** `(tradingPairId: string, interval: Interval, params?: GetCandlesticksParams) =\> Promise\<Candlestick[]\>`

Fetch OHLCV candlestick data for a trading pair.

**Parameters:**

* `tradingPairId`: string - Trading pair UUID (use `getTradingPairBySymbol` to get this)
* `interval`: `"1m" | "5m" | "15m" | "1h" | "4h" | "1d"`
* `params?`: Optional query parameters
  * `startTime?`: number - Unix timestamp in ms
  * `endTime?`: number - Unix timestamp in ms
  * `limit?`: number - Max candlesticks (default: 350, max: 500)
  * `priceType?`: `"trade" | "mark" | "reference"` - price series to chart. `"trade"` (default) is the trade-derived OHLCV series; `"mark"` is the close-only mark-price series for margin pairs (each candle's open/high/low/close are the minute's closing mark, volume `0`). `"mark"` on a spot pair returns a `400`; `"reference"` is full OHLC of the mark price over each bucket with volume `0`, for charting markets with little trading; like `"mark"` it is margin-only (a spot pair returns a `400`), and a margin pair with no reference history returns no candles

**Example:**

```tsx theme={null}
const { getTradingPairBySymbol, getCandlesticks } = useMarket();

const pair = await getTradingPairBySymbol('BTC/USDC');
const candles = await getCandlesticks(pair.id, '1h', { limit: 100 });
```

**getMarketMetadata** `(pairId: string) =\> Promise\<MarketMetadata\>`

Fetch market metadata for a trading pair including current price, volume, and statistics.

**Parameters:**

* `pairId`: string - Trading pair UUID

**Returns:** `MarketMetadata` object with market statistics

All market metadata fields are nullable. Markets without trade history will return `null` for price and volume fields.

**Fields** (all nullable):

* `lastPrice: string | null` - Most recent trade price (null if no trades)
* `lastPriceTimestamp: number | null` - Timestamp of last trade
* `high24h: string | null` - 24-hour high price (null if insufficient data)
* `low24h: string | null` - 24-hour low price (null if insufficient data)
* `volume24h: string | null` - 24-hour trading volume in base-asset units (null if insufficient data)
* `quoteVolume24h?: string | null` - 24-hour trading volume in quote-token (USDC) units
* `priceChange24h: string | null` - 24-hour price change
* `priceChangePercent24h: string | null` - 24-hour price change percentage
* `marketInitializationTimestamp: number | null` - When market was initialized

**Example with null checks:**

```typescript theme={null}
const metadata = await getMarketMetadata(pair.id);

// Always check for null before using
if (metadata.lastPrice !== null) {
  console.log(`Current price: ${metadata.lastPrice}`);
} else {
  console.log('No trades yet for this market');
}

// Use nullish coalescing for defaults
const displayPrice = metadata.lastPrice ?? 'N/A';
const displayVolume = metadata.volume24h ?? '0';
```

### Perp Market Data

For margin concepts, see [Margin](https://docs.0xmonaco.com/trading-mechanics/perps.md). For full type shapes, see the [TypeScript Market reference](https://docs.0xmonaco.com/sdk/typescript/market.md).

**getPerpMarketConfig** `(tradingPairId: string) =\> Promise\<PerpMarketConfig\>`

Static perp config: `minLeverage`, `maxLeverage`, base initial/maintenance margin ratios, `fundingIntervalSeconds`, `riskTiers`, `liquidationFeeBps`, `fundingImpactNotional`.

**getPerpMarketSummary** `(tradingPairId: string) =\> Promise\<PerpMarketSummary\>`

Aggregate snapshot: `markPrice`, `indexPrice`, 24h stats (base-asset `volume24h` and quote-token `quoteVolume24h`), `openInterest` (one-sided LONG base-asset open interest, each contract counted once; alias of `openInterestBase`), `openInterestBase`, `openInterestNotional` (base × mark, in quote units), `currentFundingRate`, `nextFundingTime`.

**getMarkPrice** `(tradingPairId: string) =\> Promise\<MarkPrice\>`

Live mark price (oracle reference). Returns `{ markPrice, oracleProvider, regime, updatedAt, ... }`.

**getIndexPrice** `(tradingPairId: string) =\> Promise\<IndexPrice\>`

Live index price with provider components. Returns `{ indexPrice, components: IndexComponent[], updatedAt }`.

**getFundingState** `(tradingPairId: string) =\> Promise\<FundingState\>`

Current and estimated next funding rate plus settlement time.

**listFundingHistory** `(tradingPairId: string, params?: ListFundingHistoryParams) =\> Promise\<ListFundingHistoryResponse\>`

Settled funding history for one pair. Cursor-paginated: `pageSize` 1 to 1000 (default 50), `pageToken`, `orderBy` (`ASC`/`DESC`, default `DESC`), optional `startTime`/`endTime`. There is no `page`: the SDK rejects one with a `ValidationError` before sending. Follow `nextPageToken` until it comes back `""`.

**getOpenInterest** `(tradingPairId: string) =\> Promise\<OpenInterest\>`

Current one-sided LONG open interest (each contract counted once) in base-asset units and notional.

**getTraderMetrics** `(tradingPairId: string) =\> Promise\<TraderMetrics\>`

Current public long/short account counts, account shares and ratio, and active-trader aggregates for a margin pair. Accounts are classified by net position across OPEN risk buckets; no account IDs are exposed. Position metrics are suppressed when either side has fewer than five margin accounts; within each active-trader window, all three counts are null if any cohort is below five users, or while the server's active-trader history does not yet reach back to the window's start (briefly, after that history is first built). Activity counts include system user IDs on liquidation and ADL fills. Responses may be reused for up to 30 seconds per pair, and ratios/shares are rounded to six decimal places.

**getTradeFlow** `(tradingPairId: string, params: GetTradeFlowParams) =\> Promise\<TradeFlow\>`

Bounded taker buy/sell quote notional and cumulative volume delta. Use `1m`, `5m`, or `1h` buckets and Unix-millisecond `startTime` (inclusive) / `endTime` (exclusive); requests are limited to seven days and 2,048 buckets. All executed trades, including liquidation and ADL fills, are included. Empty buckets are omitted and notional remains in the pair's quote asset; ratios are rounded to six decimal places.

**listLiquidationEvents** `(tradingPairId: string, params?: ListLiquidationEventsParams) =\> Promise\<ListLiquidationEventsResponse\>`

Newest-first public per-fill liquidation history for one margin pair. Optional `startTime` is inclusive and `endTime` is exclusive; page size defaults to 50 and is capped at 1,000. Event rows include wallet, position side, base size, event-time mark, and notional in the quote asset. Use [`useLiquidationFeed`](https://docs.0xmonaco.com/sdk/react/hooks/market-data#useliquidationfeed) to combine the newest page with live events and recover missed rows after reconnect.

**getLiquidationMetrics** `(tradingPairId: string, params: GetLiquidationMetricsParams) =\> Promise\<LiquidationMetrics\>`

UTC-aligned long/short summaries for `1m`, `5m`, or `1h` intervals. Requests cover at most seven days and 2,048 buckets; `eventCount` counts persisted rows, and mark notionals sum each row at its own event-time mark in quote units.

**Example — funding countdown:**

```tsx theme={null}
import { useMarket } from "@0xmonaco/react";
import { useEffect, useState } from "react";

function FundingCountdown({ tradingPairId }: { tradingPairId: string }) {
  const { getFundingState } = useMarket();
  const [next, setNext] = useState<string | null>(null);
  const [rate, setRate] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;
    const tick = async () => {
      const state = await getFundingState(tradingPairId);
      if (cancelled) return;
      setNext(state.nextFundingTime);
      setRate(state.currentFundingRate);
    };
    tick();
    const id = setInterval(tick, 15000);
    return () => { cancelled = true; clearInterval(id); };
  }, [tradingPairId, getFundingState]);

  if (!next) return null;
  const msUntil = new Date(next).getTime() - Date.now();
  return (
    <span>
      Funding in {Math.max(0, Math.floor(msUntil / 1000))}s — rate {rate}
    </span>
  );
}
```

## useInstruments

```tsx theme={null}
import { useInstruments } from "@0xmonaco/react";

const {
  instruments,
  instrumentsByPair,
  subscribed,
  error,
  clearError,
} = useInstruments();
```

A typed subscription to the public [`instrument` channel](https://docs.0xmonaco.com/sdk/typescript/websockets#instruments-channel), wrapping [`sdk.ws.instruments()`](https://docs.0xmonaco.com/sdk/typescript/websockets#instruments-channel). No authentication is required.

On subscribe the channel delivers a [snapshot](https://docs.0xmonaco.com/sdk/typescript/websockets.md#subscribe-time-snapshots) of every listed market (inactive ones flagged `isActive: false`), then live change frames. Every frame carries a market's **full current configuration** rather than a delta, so the hook keeps one terminal-state entry per trading pair: each live event replaces that market's entry outright, and each snapshot **replaces the whole map** — it is the authoritative market set at the moment it was taken.

Three `change` kinds are emitted today: `listing` (a market became tradable), `delisting` (deactivated), and `config_change`. `config_change` covers a narrow set of edits — tick size, quantity step, order-size bounds, category, and a perp market's margin ladder (`riskTiers`, with `changedFields` naming `risk_tiers`). An edit to fees emits nothing. `halt` and `unhalt` are defined on `InstrumentChangeKind` for wire-forward-compatibility with a future market-regime producer and are not emitted by the admin trading-pair path — keep a `default` branch, but do not build behavior on them yet. The subscription is torn down on unmount, and re-established when `sdk.ws` or `tradingPairId` changes.

Detected broadcast lag closes the entire socket with code `1013`, including an instrument-only connection. A dropped update may belong to a market that never emits again, so continuing from the retained tail cannot repair every key. With automatic reconnect enabled, the core client resubscribes and this hook replaces its market set when a fresh instrument snapshot arrives.

A reconnect request or `onResync` callback is not proof that the snapshot succeeded. If the snapshot is unavailable, or a decision requires independently refreshed configuration, reconcile over REST. No single endpoint carries every field this channel does, so an unfiltered hook requires multiple requests:

* `sdk.market.getPaginatedTradingPairs()`, walking `page` until you have every market (one page can omit some), covers `isActive`, `tickSize`, `quantityStepSize`, `minOrderSize`, `maxOrderSize`, `category`, `marketType`, and `maxLeverage`.
* `sdk.market.getPerpMarketConfig(tradingPairId)` covers the two remaining perp fields, which the pair list does not carry — and note the rename: this channel's `initialMarginRate` / `maintenanceMarginRate` are `initialMarginRatio` / `maintenanceMarginRatio` there. It is scoped to one pair, so following every market costs one request per perp market.

That cost is why this is a deliberate reconcile — on a suspected gap, or when a decision depends on the values — rather than something to run routinely.

Each `useInstruments` call keeps its own state, so mounting the hook in two components subscribes twice. If both resolve to the **same** channel — both omitting `tradingPairId`, or both passing the same id — the second joins a channel the client already holds, which the server does not re-snapshot; the core helper replays its cached baseline to it instead. Mounts with different filters are distinct subscriptions and each get their own server snapshot. Either way the baseline arrives asynchronously, like a real snapshot, so `instruments` is empty on the first render — render an empty list, not a spinner keyed on `subscribed`.

### Parameters

**tradingPairId** `string`

Optional trading pair UUID to filter on. Omit it to follow every market. An empty string is rejected by the underlying core helper and surfaces through `error`.

### Return Values

**instruments** `InstrumentEventData[]`

Current configuration of every market seen on the channel, ordered by `symbol` and then by `tradingPairId` — a spot and a perp market sharing a symbol keep a stable order. Sorting uses plain codepoint comparison, so the order does not shift with the runtime locale.

An inactive market stays in the list carrying `isActive: false` rather than disappearing — whether it was delisted while the subscription was live or arrived inactive in the snapshot — so a consumer can tell "no longer tradable" from "never seen" and still resolve the metadata of positions, orders and history on it. Filter on `isActive` for the tradable set. A market absent from a snapshot is a different case — it is no longer listed at all, so it leaves the list with that snapshot.

**instrumentsByPair** `Record\<string, InstrumentEventData\>`

The same entries keyed by trading pair UUID, for direct lookup.

**subscribed** `boolean`

Whether a subscription to the instrument channel is currently registered. This is **not** a connection indicator: the client accepts subscriptions while the socket is down and sends them on the next open, so `true` means "registered", not "receiving".

**error** `Error | null`

A rejected subscription request. The core helper validates its arguments synchronously — passing an empty `tradingPairId` is the reachable case — so treat this as a bad-usage signal.

It is deliberately **not** a transport signal: the client registers handlers regardless of socket state and logs malformed-frame failures internally, so connection loss and unparseable frames never appear here. Drive connection UI from the client's own status and resync callbacks instead.

**clearError** `() =\> void`

Clear the current error state.

Each entry is an `InstrumentEventData` from `@0xmonaco/types`: `change`, `tradingPairId`, `symbol`, `isActive`, `tickSize`, `quantityStepSize`, `minOrderSize`, `maxOrderSize`, `marketType`, `category`, `changedFields`, `updatedAt`, plus the perp-only `maxLeverage`, `initialMarginRate`, and `maintenanceMarginRate` (omitted on spot markets). See the [channel reference](https://docs.0xmonaco.com/reference/websockets.md#instrument) for the wire shape.

> [!NOTE]
>
> What makes the reconnect self-repairing is that the core helper delivers the snapshot as its own array rather than as per-market events, so the hook can tell where the baseline ends and drop entries missing from it. If a fresh snapshot is unavailable, the REST reconciliation above remains application-owned; reconnect does not itself fetch those endpoints.

### Example

```tsx theme={null}
import { useInstruments } from "@0xmonaco/react";

function MarketList() {
  const { instruments, error } = useInstruments();

  if (error) return <div>Error: {error.message}</div>;

  const tradable = instruments.filter((instrument) => instrument.isActive);

  return (
    <div>
      <h2>Markets</h2>
      <ul>
        {tradable.map((instrument) => (
          <li key={instrument.tradingPairId}>
            {instrument.symbol} — tick {instrument.tickSize}, step {instrument.quantityStepSize}
            {instrument.maxLeverage ? `, up to ${instrument.maxLeverage}x` : ""}
          </li>
        ))}
      </ul>
    </div>
  );
}
```

#### Following one market

```tsx theme={null}
function TickSize({ tradingPairId }: { tradingPairId: string }) {
  const { instrumentsByPair } = useInstruments(tradingPairId);
  const instrument = instrumentsByPair[tradingPairId];

  if (!instrument) return null;
  return <span>Tick size: {instrument.tickSize}</span>;
}
```

## useOHLCV

> [!NOTE]
>
> **New in v0.5.5** - Real-time candlestick data with WebSocket updates

Hook for subscribing to real-time OHLCV (Open, High, Low, Close, Volume) candlestick data via WebSocket.

### Usage

```tsx theme={null}
import { useOHLCV } from "@0xmonaco/react";

function CandlestickChart() {
  const { candlesticks, loading, subscribed, error, clearError } = useOHLCV({
    tradingPairId: "123e4567-e89b-12d3-a456-426614174000",
    tradingMode: "SPOT",
    interval: "1m",
    initialFetchParams: { limit: 100 }
  });

  if (loading) return <p>Loading candlesticks...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <p>OHLCV handler: {subscribed ? 'Registered' : 'Not registered'}</p>
      <p>Candlesticks: {candlesticks.length}</p>
      {candlesticks.map((candle, i) => (
        <div key={i}>
          O: {candle.o} H: {candle.h} L: {candle.l} C: {candle.c} V: {candle.v}
        </div>
      ))}
    </div>
  );
}
```

### Parameters

**tradingPairId** `string` *required*

UUID of the trading pair. Get from `sdk.market.getTradingPairBySymbol()`.

**tradingMode** `'SPOT' | 'MARGIN'` *required*

Trading mode for OHLCV data. Pass `"MARGIN"` for a perp market's candles.

**interval** `'1m' | '5m' | '15m' | '1h' | '4h' | '1d'` *required*

Candlestick time interval.

**initialFetchParams** `GetCandlesticksParams`

Optional parameters for initial data fetch:

* `limit` - Number of candlesticks to fetch (max 500)
* `startTime` - Start timestamp for range
* `endTime` - End timestamp for range

### Return Values

**candlesticks** `Candlestick[]`

Array of candlestick data, sorted by time (oldest first). Updates in real-time via WebSocket.

**loading** `boolean`

`true` while fetching initial candlestick data from REST API, `false` once loaded.

**subscribed** `boolean`

`true` after the OHLCV channel handler is registered, `false` otherwise. This does not report socket connection or channel delivery state.

**error** `Error | null`

Error object if subscription or fetch fails, `null` otherwise.

**clearError** `() =\> void`

Dismisses the current error. It does not retry the fetch or re-register the handler.

### Candlestick Data Structure

```typescript theme={null}
interface Candlestick {
  T: number;        // Candle start time (Unix timestamp in milliseconds)
  t: number;        // Candle end time (Unix timestamp in milliseconds)
  o: string;        // Open price
  h: string;        // High price
  l: string;        // Low price
  c: string;        // Close price
  v: string;        // Volume (base token)
  s: string;        // Symbol
  i: string;        // Interval
  n: number;        // Number of trades
  openInterestBase?: string | null;       // One-sided LONG quantity in base-asset units
  openInterestNotional?: string | null;   // Quote notional using the same sample's price
}
```

### Features

#### Hybrid Data Loading

1. **Initial REST Fetch**: Fetches historical candlesticks via REST API
2. **WebSocket Updates**: Subscribes to real-time updates after initial load
3. **Automatic Merging**: New candlesticks are automatically appended or update the last candle if timestamps match

#### Stable Subscriptions

* **No data jitter**: Prevents state updates after component unmount
* **Proper cleanup**: WebSocket automatically unsubscribes when component unmounts
* **Race condition prevention**: Cancellation flag prevents stale updates

### Basic Example

```tsx theme={null}
import { useOHLCV } from "@0xmonaco/react";

function PriceChart() {
  const { candlesticks, loading, subscribed } = useOHLCV({
    tradingPairId: "pair-uuid-here",
    tradingMode: "SPOT",
    interval: "5m"
  });

  if (loading) return <div>Loading chart...</div>;

  return (
    <div>
      <div>OHLCV handler: {subscribed ? 'Registered' : 'Not registered'}</div>
      <div>Latest Price: {candlesticks[candlesticks.length - 1]?.c}</div>
    </div>
  );
}
```

### Different Time Intervals

```tsx theme={null}
function MultiTimeframeChart() {
  const oneMinute = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1m"
  });

  const oneHour = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1h"
  });

  return (
    <div>
      <h3>1m Chart ({oneMinute.candlesticks.length} candles)</h3>
      <h3>1h Chart ({oneHour.candlesticks.length} candles)</h3>
    </div>
  );
}
```

### Fetch Historical Data

```tsx theme={null}
function HistoricalChart() {
  const endTime = Date.now();
  const startTime = endTime - (24 * 60 * 60 * 1000); // 24 hours ago

  const { candlesticks, loading } = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1h",
    initialFetchParams: {
      startTime,
      endTime,
      limit: 100
    }
  });

  if (loading) return <div>Loading...</div>;

  return <div>Loaded {candlesticks.length} historical candles</div>;
}
```

### Error Handling

```tsx theme={null}
function ChartWithErrorHandling() {
  const { candlesticks, error, clearError, loading } = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1m"
  });

  if (error) {
    return (
      <div>
        <p>Failed to load chart: {error.message}</p>
        <button onClick={clearError}>Dismiss Error</button>
      </div>
    );
  }

  if (loading) return <div>Loading...</div>;

  return <div>{candlesticks.length} candles loaded</div>;
}
```

### Display Latest Price

```tsx theme={null}
function LivePrice() {
  const { candlesticks, subscribed } = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1m"
  });

  const latestCandle = candlesticks[candlesticks.length - 1];

  if (!latestCandle) return <div>No data</div>;

  return (
    <div>
      <h2>${latestCandle.c}</h2>
      <p>
        O: {latestCandle.o} H: {latestCandle.h} L: {latestCandle.l}
      </p>
      <p>OHLCV handler: {subscribed ? 'Registered' : 'Not registered'}</p>
    </div>
  );
}
```

### Calculate Price Change

```tsx theme={null}
function PriceChange() {
  const { candlesticks } = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1d"
  });

  const [previousCandle, currentCandle] = candlesticks.slice(-2);
  if (!previousCandle || !currentCandle) return <div>Not enough data</div>;

  const current = parseFloat(currentCandle.c);
  const previous = parseFloat(previousCandle.c);
  const change = ((current - previous) / previous) * 100;

  return (
    <div>
      <p>24h Change: {change.toFixed(2)}%</p>
      <p style={{ color: change >= 0 ? 'green' : 'red' }}>
        ${current.toFixed(2)}
      </p>
    </div>
  );
}
```

### Integration with Chart Libraries

```tsx theme={null}
import { useOHLCV } from "@0xmonaco/react";
import { Chart } from "react-chartjs-2";

function TradingViewChart() {
  const { candlesticks, loading } = useOHLCV({
    tradingPairId: "pair-uuid",
    tradingMode: "SPOT",
    interval: "1h",
    initialFetchParams: { limit: 100 }
  });

  if (loading) return <div>Loading chart...</div>;

  const chartData = {
    labels: candlesticks.map(c => new Date(c.T).toLocaleTimeString()),
    datasets: [{
      label: 'Price',
      data: candlesticks.map(c => parseFloat(c.c)),
      borderColor: 'rgb(75, 192, 192)',
      tension: 0.1
    }]
  };

  return <Chart type="line" data={chartData} />;
}
```

### Notes

* The hook automatically manages WebSocket subscriptions and cleanup
* Candlesticks are updated in real-time when the WebSocket receives new events
* If the last candlestick has the same timestamp as an incoming update, it's replaced; otherwise, a new candle is appended
* Component unmounting automatically unsubscribes from the WebSocket channel

### See Also

* [WebSocket API](https://docs.0xmonaco.com/reference/websockets.md#ohlcv) - Low-level OHLCV WebSocket channel
* [TypeScript SDK](https://docs.0xmonaco.com/sdk/typescript/websockets.md) - Core SDK WebSocket methods
* [useMarket](https://docs.0xmonaco.com/sdk/react/hooks/market-data.md#usemarket) - Get trading pair metadata

## useOrderbook

```tsx theme={null}
import { useOrderbook } from "@0xmonaco/react";

const { 
  orderbook,
  subscribed,
  error,
  clearError
} = useOrderbook(tradingPairId, tradingMode, magnitude, quotationMode, depth?);
```

### Parameters

**tradingPairId** `string` *required*

Trading pair UUID. Get this from `sdk.market.getTradingPairBySymbol('BTC/USDC')`.

**tradingMode** `'SPOT' | 'MARGIN'` *required*

Trading mode for the orderbook subscription. Use `'SPOT'` for spot markets and `'MARGIN'` for perp markets.

**magnitude** `number` *required*

Price level grouping magnitude. Valid values: `0.000001`, `0.00001`, `0.0001`, `0.001`, `0.01`, `0.1`, `1`, `10`, `100`, `1000`, `10000`

**quotationMode** `'BASE' | 'QUOTE'` *required*

Quantity denomination. `BASE` shows quantities in base token, `QUOTE` multiplies by price.

**depth** `number` (default: 10)

Price levels per side for the initial REST read (max: 1000). The live stream replaces that read within moments with its own grouped book, so a deeper read only helps the first render. Above 100 levels each mount or parameter change also draws on the [deep orderbook read budget](https://docs.0xmonaco.com/developers/rate-limits.md#deep-orderbook-reads). For a full live book, use [`useDepth`](#usedepth).

### Return Values

**orderbook** `OrderbookEvent | null`

Current orderbook snapshot with bids, asks, best prices, and volumes

**Properties:**

* `tradingPairId`: Trading pair ID (UUID)
* `tradingMode`: "SPOT" | "MARGIN"
* `bids`: Array of `{ price, quantity, orderCount }`
* `asks`: Array of `{ price, quantity, orderCount }`
* `bestBid` (optional): Best bid price
* `bestAsk` (optional): Best ask price
* `bidVolume` (optional): Total bid volume
* `askVolume` (optional): Total ask volume
* `priceChange`: Price change info (optional) - `{ side, oldPrice?, newPrice?, levelRemoved, levelAdded }`
* `baseDecimals`: Base token decimals
* `quoteDecimals`: Quote token decimals
* `timestamp`: Snapshot timestamp
* `sequence`: Sequence number; its basis can change across snapshots, so compare it only between updates that follow the same snapshot
* `bookSeq`, `prevBookSeq` (optional): the book's sequence, as decimal strings, when the server renders the book from the market-data service (see [Book sequence](https://docs.0xmonaco.com/sdk/typescript/websockets.md#book-sequence))

**subscribed** `boolean`

Whether currently subscribed to real-time updates

**error** `Error | null`

Connection or subscription error if any

**clearError** `() =\> void`

Clear the current error

### Example

```tsx theme={null}
import { useOrderbook, useMarket } from "@0xmonaco/react";
import { useEffect, useState } from "react";

function OrderbookDisplay() {
  const { getTradingPairBySymbol } = useMarket();
  const [tradingPairId, setTradingPairId] = useState<string | null>(null);
  
  useEffect(() => {
    getTradingPairBySymbol('BTC/USDC').then(pair => {
      setTradingPairId(pair.id);
    });
  }, []);
  
  const { orderbook, subscribed, error } = useOrderbook(
    tradingPairId ?? '',
    'SPOT',
    1,        // Group by $1 price levels
    'BASE',   // Show quantities in base token
    20        // 20 levels each side
  );
  
  if (!tradingPairId) return <div>Loading pair...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!orderbook) return <div>Loading orderbook...</div>;
  
  return (
    <div>
      <h2>Orderbook {subscribed ? '(live)' : '(connecting...)'}</h2>
      <p>Spread: ${(Number(orderbook.bestAsk) - Number(orderbook.bestBid)).toFixed(2)}</p>
      
      <div style={{ display: 'flex', gap: '2rem' }}>
        <div>
          <h3>Bids</h3>
          {orderbook.bids.slice(0, 10).map((level, i) => (
            <div key={i} style={{ color: 'green' }}>
              {level.quantity} @ ${level.price}
            </div>
          ))}
        </div>
        
        <div>
          <h3>Asks</h3>
          {orderbook.asks.slice(0, 10).map((level, i) => (
            <div key={i} style={{ color: 'red' }}>
              {level.quantity} @ ${level.price}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}
```

### How It Works

1. **Initial Load**: Fetches orderbook snapshot via REST API
2. **Real-time Updates**: Subscribes to WebSocket for live updates
3. **Auto-cleanup**: Unsubscribes when component unmounts or params change

The hook combines REST and WebSocket for the best experience - instant data on load, real-time updates after.

## useDepth

```tsx theme={null}
import { useDepth } from "@0xmonaco/react";

const { book, subscribed, lastResync, error } = useDepth(tradingPairId, tradingMode);
```

Wraps [`sdk.ws.depth`](https://docs.0xmonaco.com/sdk/typescript/websockets#depth-channel): one market's exact, ungrouped book, up to 1000 levels per side, from the `depth` channel. The subscribe-time snapshot is the base, linked updates are applied to it, and a gap rebuilds it from a fresh snapshot. For a grouped book, use [`useOrderbook`](#useorderbook); for only the top of book, [`useBbo`](#usebbo).

### Parameters

**tradingPairId** `string` *required*

Trading pair UUID. Get this from `sdk.market.getTradingPairBySymbol('BTC/USDC')`.

**tradingMode** `'SPOT' | 'MARGIN'` *required*

The market's trading mode.

### Return Values

**book** `DepthBook | null`

The current book, or `null` until the first snapshot arrives and while the book is being rebuilt.

* `tradingPairId`, `tradingMode`
* `bookSeq`: the book's sequence, a u64 as a decimal string
* `bids`, `asks`: arrays of `{ price, quantity, orderCount }`, best first
* `bidsComplete`, `asksComplete`: `false` when levels beyond the last one held are unknown
* `stale`: the latest update was published while the service lagged the exchange
* `timestampMs` (optional): exchange time of the latest update

**subscribed** `boolean`

Whether a subscription is active.

**lastResync** `'gap' | 'snapshot_unavailable' | 'truncated' | 'disconnected' | null`

Why the book was last discarded to be rebuilt, if it has been. See [`LocalBookOptions`](https://docs.0xmonaco.com/sdk/typescript/websockets#depth-channel).

**error** `\{ code: string; message: string \} | null`

The server's rejection of the subscription, if any. An invalid trading pair ID or mode is reported here with code `INVALID_ARGUMENT`.

### Example

```tsx theme={null}
import { useDepth } from "@0xmonaco/react";

function DepthLadder({ tradingPairId }: { tradingPairId: string }) {
  const { book, error } = useDepth(tradingPairId, "SPOT");

  if (error) return <div>Error: {error.message}</div>;
  if (!book) return <div>Loading book...</div>;

  return (
    <div>
      {book.stale && <p>Market data is delayed</p>}
      {book.asks.slice(0, 10).reverse().map((level) => (
        <div key={level.price} style={{ color: "red" }}>{level.quantity} @ {level.price}</div>
      ))}
      {book.bids.slice(0, 10).map((level) => (
        <div key={level.price} style={{ color: "green" }}>{level.quantity} @ {level.price}</div>
      ))}
    </div>
  );
}
```

## useBbo

```tsx theme={null}
import { useBbo } from "@0xmonaco/react";

const { bbo, subscribed, lastResync, error } = useBbo(tradingPairId, tradingMode);
```

Wraps [`sdk.ws.bbo`](https://docs.0xmonaco.com/sdk/typescript/websockets#bbo-channel): one market's top of book from the `bbo` channel, which sends a frame whenever the best bid or ask price or size changes. Takes the same `tradingPairId` and `tradingMode` as [`useDepth`](#usedepth), and returns the same `subscribed`, `lastResync` (why the top of book was last discarded to be rebuilt) and `error` fields.

**bbo** `Bbo | null`

The current top of book, or `null` until the first snapshot arrives and while it is being rebuilt.

* `tradingPairId`, `tradingMode`
* `bookSeq`: the book's sequence, a u64 as a decimal string
* `bestBid`, `bidQty`, `bestAsk`, `askQty`: decimal strings, or `null` for an empty side
* `stale`: the latest update was published while the service lagged the exchange
* `timestampMs` (optional): exchange time of the latest update

```tsx theme={null}
import { useBbo } from "@0xmonaco/react";

function Spread({ tradingPairId }: { tradingPairId: string }) {
  const { bbo } = useBbo(tradingPairId, "MARGIN");
  if (!bbo || bbo.bestBid === null || bbo.bestAsk === null) return <span>—</span>;
  return <span>{bbo.bestBid} / {bbo.bestAsk}</span>;
}
```

## useTradeFeed

```tsx theme={null}
import { useTradeFeed } from "@0xmonaco/react";

const { 
  trades,
  subscribed,
  fetchingInitialState,
  error
} = useTradeFeed(tradingPairId);
```

### Parameters

**tradingPairId** `string` *required*

Trading pair UUID. Get this from `sdk.market.getTradingPairBySymbol('ETH/USDC')`.

> [!NOTE]
>
> The trade feed maintains a **fixed limit of 50 trades**. This limit is not configurable and is enforced internally to optimize memory and performance.

### Return Values

**trades** `TradeEvent[]`

Array of recent trades (newest first). Fixed at 50 trades maximum.

**subscribed** `boolean`

Whether currently subscribed to the trading pair. Will be false if SDK is not yet initialized.

**fetchingInitialState** `boolean`

Whether the initial trades are being fetched via REST API.

**error** `Error | null`

WebSocket error if any

### Example

```tsx theme={null}
import { useTradeFeed, useMarket } from "@0xmonaco/react";
import { useEffect, useState } from "react";

function TradeFeed() {
  const { getTradingPairBySymbol } = useMarket();
  const [tradingPairId, setTradingPairId] = useState<string | null>(null);
  
  useEffect(() => {
    getTradingPairBySymbol('ETH/USDC').then(pair => {
      setTradingPairId(pair.id);
    });
  }, []);
  
  const { trades, subscribed, error } = useTradeFeed(tradingPairId ?? '');
  
  if (!tradingPairId) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <h2>Recent Trades {subscribed ? '(live)' : '(connecting...)'}</h2>
      <ul>
        {trades.map(trade => (
          <li key={trade.data.tradeId}>
            {trade.data.makerSide} {trade.data.quantity} @ {trade.data.price}
          </li>
        ))}
      </ul>
    </div>
  );
}
```

## useLiquidationFeed

`useLiquidationFeed` returns the newest persisted liquidation events for one margin pair and merges the public WebSocket stream into the same list.

```tsx theme={null}
import { useLiquidationFeed } from "@0xmonaco/react";

function LiquidationFeed({ tradingPairId }: { tradingPairId: string }) {
  const { events, isLoading, error } = useLiquidationFeed(tradingPairId);

  if (isLoading) return <p>Loading liquidation history…</p>;
  if (error) return <p>Could not load liquidation events: {error.message}</p>;

  return (
    <ol>
      {events.map(({ data }) => (
        <li key={data.eventId}>
          {data.side} {data.positionSize} · {data.markNotional} {data.quoteAssetSymbol} · {data.wallet}
        </li>
      ))}
    </ol>
  );
}
```

The hook requests the newest REST page on mount, even before the WebSocket connects, and subscribes to `market_liquidations:{tradingPairId}` for live updates. When the server confirms or restores the subscription, it pages through the full gap from the latest seen event timestamp; if there is no checkpoint, it reads only the newest page. A gap read stops once it has fetched the newest 500 events, the most the hook keeps. Because storage can lag the live stream, the hook repeats the read from the newest stored event it has seen, waiting 1, 2, 4 and then 8 seconds after each read finishes (so roughly 1, 3, 7 and 15 seconds after the first). It stops early once stored history contains the first live event received after the subscription was confirmed, or, when no live event has arrived, once two repeats in a row find nothing new. It merges recovered rows with live updates, deduplicates by `eventId`, and keeps the newest 500 events, dropping older ones. The stream has no snapshot. Timestamps are Unix milliseconds, sizes and prices are decimal strings, and `markNotional` uses the event's captured mark in the quote asset.

## useFees

```tsx theme={null}
import { useFees } from "@0xmonaco/react";

const { simulateFees, getMyFeeTier, getFeeSchedule } = useFees();
```

### Methods

**simulateFees** `(params: SimulateFeeParams) =\> Promise\<SimulateFeeResponse\>`

Calculate exact fees for an order before placing it. Requires authentication.

**Parameters:**

* `tradingPairId`: string - Trading pair UUID
* `side`: "BUY" | "SELL" - Order side
* `price`: string - Price per unit
* `quantity`: string - Quantity to trade
* `orderType`: "LIMIT" | "MARKET" (optional) - Defaults to LIMIT behaviour if omitted. Pass `"MARKET"` to get a slippage-adjusted `buyOrderLockAmount`
* `slippageToleranceBps`: number (optional, 0–1000) - Slippage tolerance in basis points. Only valid with `orderType: "MARKET"`. Default: `1000` (10%), matching the protective price band

**Returns:**

* `notional`: string - Total order value (price × quantity)
* `monacoTakerFee`: string - Monaco protocol taker fee
* `monacoTakerFeeBpsExact`: string | null - Monaco taker rate, from your tier or the assigned or custom rate on your wallet, as a decimal bps string (e.g. `"6.5"`) — the exact rate the ledger charges this caller; `monacoTakerFee` is `price × quantity` at this rate, truncated to the quote token's decimals
* `monacoMakerRebateBpsExact`: string | null - Monaco maker rate, from your tier or the assigned or custom rate on your wallet, as a decimal bps string (negative = rebate, e.g. `"-1"`) — the exact rate the ledger credits this caller
* `monacoMakerRebate`: string - Monaco maker rebate (negative = rebate)
* `applicationTakerFee`: string - Application taker fee
* `applicationTakerFeeBps`: number - Application fee in basis points
* `applicationName`: string | null - Application name (if applicable)
* `totalTakerFees`: string - Total fees for taker (monaco + application)
* `takerTotalPayment`: string - Total amount taker must pay
* `makerTotalReceipt`: string - Total amount received if the order fills as the maker: notional minus the maker fee (a rebate adds) minus `applicationMakerFee`
* `applicationMakerFee?`: string - Application maker add-on if the order fills as the maker, paid on top of the maker fee or rebate
* `applicationMakerFeeBps?`: number - Application maker add-on in basis points: the rate an order placed now would store
* `buyOrderLockAmount`: string | null - Amount to lock for BUY orders. For MARKET orders includes slippage buffer
* `maxQuantity`: string | null - Maximum quantity affordable at the given price, after fees. Use this for the 100% balance slider
* `maxQuantityRaw`: string | null - Same value in raw (smallest unit) format
* `slippageToleranceBps`: number | null - Slippage tolerance used in the calculation, echoed back. `null` for LIMIT orders

Every amount is truncated toward zero to the quote token's decimals, as the matching engine charges it: `notional`, each fee and rebate, and the totals `totalTakerFees`, `takerTotalPayment` and `makerTotalReceipt`, which are sums of the truncated parts. A rebate smaller than one atomic unit reads `0`.

**getMyFeeTier** `(params: GetMyFeeTierParams) =\> Promise\<GetMyFeeTierResponse\>`

Look up your current fee tier, the rates that apply to you on a pair and why, rolling 14-day volumes, and the pair's six-row fee schedule. Requires authentication. The volumes and the tier they earn are wallet-wide: they sum the wallet's trades across every application it trades through, so they read the same from any application.

**Parameters:**

* `tradingPairId`: string - Trading pair UUID whose fee schedule to return

**Returns:**

* `currentTierLevel`: number - Tier `1` (lowest volume) through `6` (highest) your rates come from, or the tier `weightedVolume14d` earns when `rateSource` is `custom`
* `weightedVolume14d`: string - Weighted 14-day volume (perp + 2.5× spot) the tier resolves from
* `spotVolume14d`: string - Rolling 14-day spot volume
* `perpVolume14d`: string - Rolling 14-day perp volume
* `volumeToNextTier`: string | null - Additional weighted volume needed to reach the next tier, counted from the volume the current tier resolved from; omitted at tier 6 and unless `rateSource` is `volume`
* `nextTierLevel`: number | null - Next tier level; omitted at tier 6 and unless `rateSource` is `volume`
* `feeSchedule`: FeeTierScheduleRow\[] - The pair's six rows, ascending by tier. Each row: `tierLevel` (number), `minVolumeThreshold` (string), `makerFeeBps` (string — human bps, negative = rebate), `takerFeeBps` (string — human bps)
* `rateSource`: `"volume"` | `"tier_override"` | `"custom"` - Why this rate applies: the tier your 14-day volume earns, an assigned tier, or custom rates assigned to your wallet. See [Custom and assigned rates](https://docs.0xmonaco.com/fees.md#custom-and-assigned-rates)
* `makerFeeBps`: string - Effective maker rate for the requested pair, in decimal basis points; negative is a rebate, positive is a fee
* `takerFeeBps`: string - Effective taker rate for the requested pair, in decimal basis points

**getFeeSchedule** `(tradingPairId: string) =\> Promise\<GetFeeScheduleResponse\>`

Read a trading pair's Monaco fee tier schedule. Public: needs no authentication, so it works before the user signs in.

**Parameters:**

* `tradingPairId`: string - Trading pair UUID whose fee schedule to return

**Returns:**

* `tradingPairId`: string - The trading pair the schedule belongs to
* `feeSchedule`: FeeTierScheduleRow\[] - The pair's tiers, ascending by tier level. Each row: `tierLevel` (number), `minVolumeThreshold` (string), `makerFeeBps` (string — human bps, negative = rebate), `takerFeeBps` (string — human bps)

### Example

```tsx theme={null}
import { useFees, useMarket } from "@0xmonaco/react";
import { useState, useEffect } from "react";

function FeeCalculator() {
  const { getTradingPairBySymbol } = useMarket();
  const { simulateFees } = useFees();
  const [fees, setFees] = useState(null);

  const calculateFees = async () => {
    const pair = await getTradingPairBySymbol('BTC/USDC');

    // LIMIT order — exact fee calculation
    const result = await simulateFees({
      tradingPairId: pair.id,
      side: "BUY",
      price: "50000",
      quantity: "0.1"
    });

    setFees(result);
  };

  return (
    <div>
      <button onClick={calculateFees}>Calculate Fees</button>
      {fees && (
        <div>
          <p>Order Value: ${fees.notional}</p>
          <p>Total Fees: ${fees.totalTakerFees}</p>
          <p>You Pay: ${fees.takerTotalPayment}</p>
          {/* 100% slider — use maxQuantity instead of balance / price */}
          <p>Max quantity you can buy: {fees.maxQuantity}</p>
        </div>
      )}
    </div>
  );
}
```

### Fee Breakdown

Monaco uses a maker-taker fee model:

| Role | Fee Type | Description |
| - | - | - |
| **Taker** | Positive fee | Pay fees when taking liquidity (market orders, crossing spread) |
| **Maker** | Rebate | Earn rebates when providing liquidity (limit orders in book) |

Use `simulateFees` before placing orders to show users exact costs upfront.


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