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

> Query trading pairs and market data

```typescript theme={null}
import { MonacoSDK } from "@0xmonaco/core";

// Initialize SDK with wallet client (see Initialization guide)

// Trading pairs
await sdk.market.getPaginatedTradingPairs(params?);        // optional: { category: "crypto" | "equities" | "commodities" | "fx" }
await sdk.market.getTradingPair(tradingPairId);
await sdk.market.getTradingPairBySymbol(symbol);

// Market screener
await sdk.market.getScreener(params?);                     // optional: { category: "crypto" | "equities" | "commodities" | "fx" }

// Exchange-wide life-to-date statistics (no auth)
await sdk.market.getMarketStats();

// Market metadata
await sdk.market.getMarketMetadata(pairId);

// Candlestick data (requires trading pair UUID)
const pair = await sdk.market.getTradingPairBySymbol('BTC/USDC');
await sdk.market.getCandlesticks(pair.id, interval, { startTime, endTime, limit, priceType }); // priceType: "trade" (default) | "mark"

// Orderbook snapshot (REST API)
await sdk.orderbook.getOrderbook(tradingPairId, options?);

// Historical trades (REST API)
await sdk.trades.getTrades(tradingPairId, options?);
await sdk.trades.getTradeById(tradeId);
```

## Trading Pairs

An **operator-managed market** is public only while its launch's **durable runtime projection** reads `live` — the coordinator's own launch phase is not what publishes it, so a launch whose phase is already `live` stays hidden whenever its runtime projection is anything else, halted included. Until then it is excluded from pair discovery, pair detail, the screener and the public price reads, and asking for inactive markets does not surface it either. A pair with no managed launch is an ordinary instrument and keeps its existing visibility, so this changes nothing about the markets that trade today. The maker side of such a launch is driven through [`sdk.managedMarkets`](/sdk/typescript/managed-markets).

<ResponseField name="getPaginatedTradingPairs" type="(params?) => Promise<GetTradingPairsResponse>">
  Fetch paginated list of trading pairs.

  **Parameters:** all optional

  * `page?`: number
  * `pageSize?`: number
  * `category?`: `"crypto" | "equities" | "commodities" | "fx"` — filter by asset class

  Each `TradingPair` includes a `category` field indicating its asset class,
  and `baseAssetName` / `quoteAssetName` — the human-readable asset display
  names from the asset registry (e.g. `"Robinhood Markets"` for `HOOD`,
  `"USDC"` for `USDC`). Use them for name-based market search (a query like
  "Robinhood" can match the `HOOD` market) and to render display names next to
  tickers. Also present on `getTradingPair` and `getTradingPairBySymbol`.

  `isolatedOnly` (boolean) marks an isolated-only market: cross margin may only
  reduce a position there. A `CROSS` order that is not reduce-only is rejected
  with the error code `CROSS_MARGIN_NOT_ALLOWED`; open the position with
  `marginMode: "ISOLATED"`. Also present on `getTradingPair` and
  `getTradingPairBySymbol`.
</ResponseField>

<ResponseField name="getTradingPair" type="(tradingPairId: string) => Promise<TradingPair>">
  Fetch a single trading pair by its UUID. Returns the unwrapped `TradingPair`. Rejects with an `APIError` (`404`) when no pair with that UUID exists — it never resolves `undefined`.

  **Parameters:**

  * `tradingPairId`: string - Trading pair UUID

  **Example:**

  ```typescript theme={null}
  const pair = await sdk.market.getTradingPair('a1b2c3d4-...');
  console.log(`${pair.symbol} tick size: ${pair.tickSize}`);
  console.log(`${pair.symbol} quantity step: ${pair.quantityStepSize}`);
  ```
</ResponseField>

<ResponseField name="getTradingPairBySymbol" type="(symbol: string, marketType?: TradingMode) => Promise<TradingPair | undefined>">
  Get a specific trading pair by symbol. Resolves `undefined` on a miss — it never throws for an unknown symbol, so guard the result before dereferencing. Note the failure modes are opposite: `getTradingPair` rejects, `getTradingPairBySymbol` resolves `undefined`. The lookup scans only the first 100 pairs (optionally filtered by `marketType`); use `getPaginatedTradingPairs` directly if the listing ever exceeds that.

  **Parameters:**

  * `symbol`: string - Trading pair symbol (e.g. `"BTC/USDC"`)
  * `marketType?`: `TradingMode` - Restrict the lookup to `"SPOT"` or `"MARGIN"` pairs
</ResponseField>

## Market Screener

<ResponseField name="getScreener" type="(params?: GetScreenerParams) => Promise<GetScreenerResponse>">
  Fetch the paginated market screener. Each row carries 1h/24h/7d quote-volume and price-change windows plus a 7-day daily snapshot. Results are sorted by 24h quote volume descending, with nulls last.

  **Parameters:** `params?` - all optional

  * `page?`: number - Page number (min 1, default 1)
  * `pageSize?`: number - Items per page (min 1, max 100, default 50)
  * `marketType?`: `"SPOT" | "MARGIN"` - Filter by market type
  * `isActive?`: boolean - Filter by active status; defaults to `true` so delisted pairs are hidden. The managed-market fence is an **additional** gate rather than an override of this filter: a managed launch is hidden until its runtime projection is live, and once live its pair is active, so `isActive: false` still excludes it
  * `category?`: `"crypto" | "equities" | "commodities" | "fx"` - Filter by asset class

  **Returns:** `GetScreenerResponse`:

  * `items`: `ScreenerItem[]`
  * `page`: number
  * `pageSize`: number
  * `total`: number
  * `totalPages`: number

  Each `ScreenerItem`:

  * `tradingPairId`: string - Trading pair UUID
  * `symbol`: string - Trading pair symbol (e.g. "BTC/USDC")
  * `category`: string - Asset class: `"crypto"`, `"equities"`, `"commodities"`, or `"fx"`
  * `baseIconUrl`: string - Base token icon URL
  * `quoteIconUrl`: string - Quote token icon URL
  * `lastPrice`: string | null - Most recent close price (null if no trades yet)
  * `lastPriceTimestamp`: string | null - Timestamp of the last candle (ms since epoch, as string; null if no trades yet)
  * `quoteVolume1h`: string | null - Quote-token volume over the last 1 hour (null if insufficient history)
  * `quoteVolume24h`: string | null - Quote-token volume over the last 24 hours (null if insufficient history)
  * `quoteVolume7d`: string | null - Quote-token volume over the last 7 days (null if insufficient history)
  * `priceChangePercent1h`: string | null - Percent price change over the last 1 hour (null if insufficient history)
  * `priceChangePercent24h`: string | null - Percent price change over the last 24 hours (null if insufficient history)
  * `priceChangePercent7d`: string | null - Percent price change over the last 7 days (null if insufficient history). Screener rows carry percent changes only — for an absolute 24h change, read `priceChange24h` from `getMarketMetadata` / `getPerpMarketSummary`, or estimate it from `lastPrice` and `priceChangePercent24h` (approximate, since the percentage is rounded)
  * `snapshot7d`: `ScreenerSnapshotPoint[]` - Up to 7 UTC-day buckets (oldest first); empty when less than 1 day of history. Each point is `{ bucketStart, quoteVolume, priceChangePercent, openInterestNotionalAvg }`. The daily average is `string | null` and needs at least 95% of the completed UTC day's 1440 minutes (1368) to be valued.
  * `totalQuoteVolumeLtd`: string - Cumulative quote-token volume since inception (decimal string; `"0"` if no trades)
  * `totalTradeCountLtd`: number - Cumulative trade count since inception (`0` if no trades)
  * `latestFundingRate`: string | null - Latest capped settled funding rate (decimal fraction per window); `null` for spot pairs and perps with no settled window yet
  * `lastFundingTime`: string | null - Close time of the latest settled funding window (ms since epoch, as string); `null` for spot pairs and perps with no settled window yet
  * `estimatedNextFundingRate`: string | null - Estimated rate for the in-progress funding window (decimal fraction per window), the same value as `getFundingState().estimatedNextFundingRate`; `null` for spot pairs and perps whose current window has no samples yet. It can differ from the rate that settles: see the note under `getFundingState`
  * `openInterestBase`: string | null - Gross base-asset open interest (longs plus shorts); `null` for spot pairs, `"0"` for a margin pair with no open positions
  * `openInterestNotional`: string | null - Gross open-interest notional (longs plus shorts) in quote units, valued at the pair's latest trade price (a snapshot — for a mark-accurate notional use `getPerpMarketSummary` or `getOpenInterest`); `null` for spot pairs, `"0"` for a margin pair with no open positions
  * `openInterestNotionalAvg1h`, `openInterestNotionalAvg24h`, `openInterestNotionalAvg7d`: string | null - Arithmetic averages of the valued completed UTC minute observations in the last 60, 1440, or 10080 minutes ending at the current minute boundary, published once at least 95% of those minutes (57, 1368, or 9576) are valued. Each observation is gross open position size multiplied by latest trade price in quote units, the same price `openInterestNotional` uses; a minute without a trade price is unvalued rather than zero. `null` for spot or below that coverage; known zero is `"0"`. No historical backfill; windows warm after deployment.

  **Example:** Install `decimal.js` (`npm install decimal.js`) to compute surge directly from decimal strings.

  ```typescript theme={null}
  import Decimal from "decimal.js";

  const screener = await sdk.market.getScreener({ marketType: "MARGIN", pageSize: 50 });

  screener.items.forEach((row) => {
    const average7d = row.openInterestNotionalAvg7d;
    const surge = row.openInterestNotionalAvg24h !== null && average7d !== null && new Decimal(average7d).gt(0)
      ? new Decimal(row.openInterestNotionalAvg24h).div(average7d).toString()
      : null;
    console.log(`${row.symbol}: 24h/7d-average OI ${surge ?? "n/a"}`);
  });
  ```
</ResponseField>

## Market Metadata

<ResponseField name="getMarketMetadata" type="(tradingPairId: string) => Promise<MarketMetadata>">
  Get comprehensive market metadata including current price and 24-hour statistics

  **Parameters:**

  * `tradingPairId`: string - Trading pair UUID

  **Returns:** `MarketMetadata`:

  * `symbol`: string - Trading pair symbol (e.g., "BTC/USDC")
  * `lastPrice`: string - Current price (8 decimal precision)
  * `lastPriceTimestamp`: number - Unix timestamp (ms)
  * `high24h`: string | null - 24h high price
  * `low24h`: string | null - 24h low price
  * `volume24h`: string | null - 24h trading volume in base-asset units
  * `quoteVolume24h?`: string | null - 24h trading volume in quote-token (USDC) units
  * `priceChange24h`: string | null - Absolute price change
  * `priceChangePercent24h`: string | null - Percentage change
  * `marketInitializationTimestamp`: number | null - Market start time
  * `totalBaseVolumeLtd`: string - Cumulative base-token volume since inception (decimal string; `"0"` if no trades)
  * `totalQuoteVolumeLtd`: string - Cumulative quote-token volume since inception (decimal string; `"0"` if no trades)
  * `totalTradeCountLtd`: number - Cumulative trade count since inception (`0` if no trades)

  **Example:**

  ```typescript theme={null}
  const pair = await sdk.market.getTradingPairBySymbol('BTC/USDC');
  const metadata = await sdk.market.getMarketMetadata(pair.id);

  console.log(`${metadata.symbol}: $${metadata.lastPrice}`);
  console.log(`24h Change: ${metadata.priceChangePercent24h}%`);
  console.log(`24h High: $${metadata.high24h}`);
  console.log(`24h Low: $${metadata.low24h}`);
  ```
</ResponseField>

## Exchange Statistics

<ResponseField name="getMarketStats" type="() => Promise<MarketStats>">
  Fetch exchange-wide life-to-date cumulative statistics. No authentication required.

  **Returns:** `MarketStats`:

  * `totalQuoteVolumeLtd`: string - Cumulative quote-token (notional) volume across all trading pairs, as a decimal string in display units
  * `totalTradeCountLtd`: number - Cumulative trade count across all trading pairs

  Volume is summed across all pairs; base volume is intentionally omitted because base quantities are not comparable across different base assets. Pairs with no trades contribute `"0"` / `0`.

  **Example:**

  ```typescript theme={null}
  const stats = await sdk.market.getMarketStats();
  console.log(`All-time volume: ${stats.totalQuoteVolumeLtd} USDC`);
  console.log(`All-time trades: ${stats.totalTradeCountLtd}`);
  ```
</ResponseField>

## Candlestick Data

<ResponseField name="getCandlesticks" type="(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 (for forward pagination)
    * `endTime?`: number - Unix timestamp in ms (for backward pagination)
    * `limit?`: number - Max candlesticks to return (default: 350, max: 500)
    * `priceType?`: `"trade" | "mark"` - price series to chart. `"trade"` (default) is the trade-derived OHLCV series; `"mark"` is the close-only mark-price series for margin pairs, where each candle's open/high/low/close are the minute's closing mark and volume is `0`. `"mark"` on a spot pair returns a `400`

  Each returned `Candlestick` may also include `openInterestBase` (one-sided LONG quantity in base-asset units) and `openInterestNotional` (quote-asset notional valued with the price paired to that same OI sample). Both are aligned to the candle end; the notional is not rebuilt from the candle close. They are `null` when the observation or paired price is unavailable.

  **Example:**

  ```typescript theme={null}
  const pair = await sdk.market.getTradingPairBySymbol('BTC/USDC');

  // Get latest 100 candles
  const candles = await sdk.market.getCandlesticks(pair.id, '1h', { limit: 100 });

  // Get candles for a time range
  const historicalCandles = await sdk.market.getCandlesticks(pair.id, '5m', {
    startTime: Date.now() - 24 * 60 * 60 * 1000,
    endTime: Date.now()
  });
  ```
</ResponseField>

## Perp Market Data

Perp-specific market endpoints. For margin concepts, see [Margin](/trading-mechanics/perps).

<ResponseField name="getPerpMarketConfig" type="(tradingPairId: string) => Promise<PerpMarketConfig>">
  Static configuration for a perp pair: leverage bounds, base margin ratios, funding interval, risk tiers, liquidation fee.

  **Returns — `PerpMarketConfig`:**

  * `tradingPairId`, `symbol`: string
  * `minLeverage`, `maxLeverage`: string (decimal)
  * `initialMarginRatio`, `maintenanceMarginRatio`: string
  * `fundingIntervalSeconds`: number
  * `liquidationFeeBps?`: string
  * `riskTiers`: `RiskTier[]`, ascending by `lowerBoundNotional` — each `{ tierLevel, lowerBoundNotional, maxLeverage?, initialMarginRatio, maintenanceMarginRatio, initialMarginDeduction, maintenanceMarginDeduction, maxPositionNotional? }`. `maxPositionNotional` is the most USD notional a position may hold at that tier's `maxLeverage`: the next tier's `lowerBoundNotional`, or the market's position cap on the top tier, and absent there when the market is uncapped. Below the top tier the cap is exclusive, since a notional at the next bound already sits in the next tier. `maxPositionSize` is deprecated and no longer set. `initialMarginDeduction`/`maintenanceMarginDeduction` keep the ladder floor and maintenance margin continuous at a tier bound: `IM_floor(N) = N x initialMarginRatio - initialMarginDeduction`, `MM(N) = N x maintenanceMarginRatio - maintenanceMarginDeduction` for a position of notional `N` in that tier. Actual position initial margin is the greater of `IM_floor(N)` and the selected-leverage initial margin scaled to the current mark. A position's tier is the last one whose `lowerBoundNotional` is at or below its notional. See [Margin ladders](/trading-mechanics/perps#margin-ladders).
  * `updatedAt`: string

  **Example — two-tier ladder:**

  ```typescript theme={null}
  const config = await sdk.market.getPerpMarketConfig(pair.id);
  // config.riskTiers ==
  // [
  //   { tierLevel: 1, lowerBoundNotional: "0",         maxLeverage: "50", initialMarginRatio: "0.02", maintenanceMarginRatio: "0.01", initialMarginDeduction: "0", maintenanceMarginDeduction: "0" },
  //   { tierLevel: 2, lowerBoundNotional: "100000000", maxLeverage: "25", initialMarginRatio: "0.04", maintenanceMarginRatio: "0.02", initialMarginDeduction: "2000000", maintenanceMarginDeduction: "1000000" },
  // ]
  // A $200M position falls in tier 2: MM = 200_000_000 * 0.02 - 1_000_000 = 3_000_000
  ```
</ResponseField>

<ResponseField name="getPerpMarketSummary" type="(tradingPairId: string) => Promise<PerpMarketSummary>">
  Aggregate snapshot of a perp market: mark, index, 24h stats, gross open interest, current and estimated next funding rate.

  **Returns — `PerpMarketSummary`:**

  * `tradingPairId`, `symbol`: string
  * `lastPrice`, `markPrice`, `indexPrice`: string
  * `high24h?`, `low24h?`, `priceChange24h?`, `priceChangePercent24h?`: string
  * `volume24h?`: string — 24h volume in base-asset units
  * `quoteVolume24h?`: string — 24h volume in quote-token (USDC) units
  * `openInterest`: string — gross open interest (longs plus shorts) in base-asset units (alias of `openInterestBase`)
  * `openInterestBase?`: string — gross open interest (longs plus shorts) in base-asset units
  * `openInterestNotional?`: string — gross open-interest notional in quote (USDC) units, equal to `openInterestBase` × mark price (falling back to the latest trade/close price when no mark is available)
  * `currentFundingRate?`, `estimatedNextFundingRate?`: string
  * `nextFundingTime?`: string — ISO 8601
  * `marketStatus`, `marketRegime`: string
  * `updatedAt`: string
</ResponseField>

<ResponseField name="getMarkPrice" type="(tradingPairId: string) => Promise<MarkPrice>">
  Live mark price (oracle reference used for PnL and liquidation).

  **Returns:** `{ tradingPairId, markPrice, oracleProvider, oracleEpoch?, regime, updatedAt }`
</ResponseField>

<ResponseField name="getIndexPrice" type="(tradingPairId: string) => Promise<IndexPrice>">
  Live index price (aggregated external fair-value reference used for funding).

  **Returns:** `{ tradingPairId, indexPrice, components: IndexComponent[], updatedAt }`. Each `IndexComponent` is `{ provider, price, weight?, updatedAt? }`.
</ResponseField>

<ResponseField name="getFundingState" type="(tradingPairId: string) => Promise<FundingState>">
  Current and estimated-next funding rate, plus next settlement time.

  **Returns:** `{ tradingPairId, currentFundingRate, estimatedNextFundingRate?, nextFundingTime, fundingIntervalSeconds, lastFundingTime?, updatedAt }`

  `nextFundingTime` reflects the market's actual settlement cadence and is always in the future (once a boundary passes, it is the following one), and `estimatedNextFundingRate` refreshes as new samples arrive.

  `estimatedNextFundingRate` is an estimate, not a promise. A window that ends with too few accepted samples settles at the previous settled rate instead (`0` if the market has never settled one; a first window with enough samples settles normally), so the rate that settles can differ from the last estimate. The screener (`ScreenerItem.estimatedNextFundingRate`) and the all-markets WebSocket `market_stats` row (`fundingRate`) carry the same estimate.
</ResponseField>

<ResponseField name="listFundingHistory" type="(tradingPairId: string, params?: ListFundingHistoryParams) => Promise<ListFundingHistoryResponse>">
  Historical settled funding for a single pair.

  **Parameters:** all optional

  * `startTime?`, `endTime?`: string — ISO 8601 or ms-epoch string
  * `pageSize?`: number — items per page, 1 to 1000 (default 50)
  * `pageToken?`: string — pagination cursor: omitted or `""` starts a walk from the newest settlement (the oldest under `orderBy: "ASC"`); a previous `nextPageToken` resumes it. There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`. The walk seeks on `(settledAt, id)`, so a page boundary inside one settlement instant no longer repeats or drops a row. The filters and `orderBy` must match the request that minted the token
  * `orderBy?`: `"ASC" | "DESC"` — sort direction for `settledAt`, default `DESC`

  **Returns:** `{ records: FundingRecord[], total, pageSize, nextPageToken, totalCapped }`. `nextPageToken` is `""` once the walk is exhausted — each page over-fetches one row, so an empty token is definitive; follow a non-empty one and do not drive the loop off `total`, which is bounded by the server count ceiling (`totalCapped` is `true` when more matched, making `total` a lower bound). The ergonomic `FundingRecord` surfaces `{ tradingPairId, fundingRate, cumulativeFundingPerUnit?, carried? }` — `carried` is `true` when the window settled at the carried last rate under the sparse-sample rule. The ergonomic type also carries the per-window fields — `epoch`, `fundingDeltaPerUnit`, `referencePrice` (the settlement mark price), `sampleCount`, `windowStartedAt`, `windowClosedAt`, and `settledAt` (the sort key for `listAllFundingHistory`). `fundingDeltaPerUnit` is normally `fundingRate × referencePrice`; in the one defensive case where that product cannot be represented (the engine bounds the rate below 100 % per window, so this needs a mark near the numeric ceiling), the record keeps the `fundingRate` that would have applied, settles `fundingDeltaPerUnit` as `0`, leaves `cumulativeFundingPerUnit` unchanged and still advances `epoch` — a window in which nothing was paid, not a missing window.
</ResponseField>

<ResponseField name="listAllFundingHistory" type="(params?: ListAllFundingHistoryParams) => Promise<ListAllFundingHistoryResponse>">
  Settled funding history across **every** perp market in one call, so you no longer fan out one `listFundingHistory` request per pair. No authentication required.

  **Parameters:** all optional

  * `startTime?`, `endTime?`: string — ms-epoch strings, inclusive bounds on `settledAt`
  * `pageSize?`: number — items per page, 1 to 1000 (default 50)
  * `pageToken?`: string — pagination cursor: omitted or `""` starts a walk from the newest settlement (the oldest under `orderBy: "ASC"`); a previous `nextPageToken` resumes it. There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`. The walk seeks on `(settledAt, id)`, so a page boundary inside one settlement instant no longer repeats or drops a row. The filters and `orderBy` must match the request that minted the token
  * `orderBy?`: `"ASC" | "DESC"` — sort direction for `settledAt`, default `DESC`

  **Returns:** `{ records: FundingRecord[], total, pageSize, nextPageToken, totalCapped }`. `nextPageToken` is `""` once the walk is exhausted — each page over-fetches one row, so an empty token is definitive; follow a non-empty one and do not drive the loop off `total`, which is bounded by the server count ceiling (`totalCapped` is `true` when more matched, making `total` a lower bound). Rows are sorted by the wire `settledAt` field. Each `FundingRecord` carries its own `tradingPairId`, so you can group rows by market client-side (see `listFundingHistory` for the full record shape).

  **Example:**

  ```typescript theme={null}
  const funding = await sdk.market.listAllFundingHistory({ orderBy: "DESC", pageSize: 100 });
  funding.records.forEach((r) => {
    console.log(`${r.tradingPairId}: ${r.fundingRate}${r.carried ? " (carried)" : ""}`);
  });
  ```
</ResponseField>

<ResponseField name="getOpenInterest" type="(tradingPairId: string) => Promise<OpenInterest>">
  Current gross open interest (longs plus shorts) in base-asset units and notional.

  **Returns:** `{ tradingPairId, openInterestBase, openInterestNotional, updatedAt }`
</ResponseField>

## Orderbook Snapshot

Get a point-in-time orderbook snapshot via REST API. For real-time updates, use WebSocket subscriptions.

<ResponseField name="orderbook.getOrderbook" type="(tradingPairId: string, options?) => Promise<OrderbookEvent>">
  Fetch current orderbook state for a trading pair

  **Parameters:**

  * `tradingPairId`: string - Trading pair UUID
  * `options?`: Optional settings
    * `depth?`: number - Number of price levels (default: 10, max: 100)
    * `tradingMode?`: "SPOT" | "MARGIN" - Trading mode (default: "SPOT")
    * `magnitude?`: number - Price grouping (0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000)
    * `denomination?`: "BASE" | "QUOTE" - Quantity denomination

  **Returns:**

  * `symbol`: string - Trading pair symbol
  * `tradingMode`: "SPOT" | "MARGIN" - Trading mode
  * `bids`: Array of `{ price, quantity, orderCount }`
  * `asks`: Array of `{ price, quantity, orderCount }`
  * `bestBid?`: string - Best bid price (optional)
  * `bestAsk?`: string - Best ask price (optional)
  * `bidVolume?`: string - Total bid volume (optional)
  * `askVolume?`: string - Total ask volume (optional)
  * `priceChange?`: object - Price change info (optional)
  * `baseDecimals`: number - Base token decimals
  * `quoteDecimals`: number - Quote token decimals
  * `timestamp`: string - Snapshot timestamp (UTC)
  * `sequence`: number - Sequence number

  **Example:**

  ```typescript theme={null}
  const pair = await sdk.market.getTradingPairBySymbol('BTC/USDC');

  const orderbook = await sdk.orderbook.getOrderbook(pair.id, {
    depth: 20,
    magnitude: 1  // Group by $1 price levels
  });

  console.log(`Best bid: ${orderbook.bestBid}`);
  console.log(`Best ask: ${orderbook.bestAsk}`);
  console.log(`Spread: ${Number(orderbook.bestAsk) - Number(orderbook.bestBid)}`);
  ```
</ResponseField>

## Historical Trades

Fetch recent trade history for a trading pair. For real-time trade updates, use WebSocket subscriptions.

<ResponseField name="trades.getTrades" type="(tradingPairId: string, options?) => Promise<{ trades: TradeEvent[]; nextPageToken: string }>">
  Fetch one page of historical trades for a trading pair (newest first)

  **Cursor paged.** The page number was removed: `trades` is appended to
  continuously, so an offset silently re-served the newest trades as fills
  landed mid-walk. Omit `pageToken` to start, then follow `nextPageToken` until
  it comes back empty. Sending `page` is rejected before the request leaves the
  SDK, and the REST API answers `400` if it reaches the wire.

  A walk reaches the end of the archiver's retention window, not the end of the
  pair's history — the endpoint's long-standing scope. There is no public walk
  over a pair's archived history, so do not treat
  [`getUserTrades`](/sdk/typescript/profile) as a continuation of this one: it is
  authenticated and returns only trades where **you** were maker or taker, so
  following it to extend a public tape would silently drop every fill between two
  other parties. Use it for your own archived fills. A single archived trade is
  still publicly retrievable by id via `getTradeById`.

  **Parameters:**

  * `tradingPairId`: string - Trading pair UUID
  * `options?`: Optional settings
    * `pageSize?`: number - Max records to return (default: 25, max: 1000)
    * `pageToken?`: string - Cursor from a previous response's `nextPageToken`; omit to start at the most recent trade

  **Returns:** `{ trades, nextPageToken }`, where `nextPageToken` is empty
  exactly when the walk is finished (the server over-fetches one row to prove
  it, so no confirming extra request is needed), and `trades` is an array of
  `TradeEvent`:

  * `eventType`: "trade"
  * `tradingPairId`: string - Trading pair UUID
  * `tradingMode`: "SPOT" | "MARGIN"
  * `data`:
    * `tradeId`: string - Unique trade ID
    * `price`: string - Execution price
    * `quantity`: string - Executed quantity
    * `makerSide`: "BUY" | "SELL" - Side of the maker order
    * `executedAt`: string - ISO 8601 date-time timestamp (UTC), empty string if the trade has no recorded execution time

  **Timestamp format:** `executedAt` is an ISO-8601 / RFC 3339 date-time string with an explicit `+00:00` UTC offset (e.g. `2025-01-15T10:30:00+00:00`). Any standard parser, including `new Date(...)`, handles it.

  **Example:**

  ```typescript theme={null}
  const pair = await sdk.market.getTradingPairBySymbol('BTC/USDC');

  // Last 50 trades
  const { trades } = await sdk.trades.getTrades(pair.id, { pageSize: 50 });

  trades.forEach(trade => {
    console.log(`${trade.data.makerSide} ${trade.data.quantity} @ ${trade.data.price}`);
  });

  // Walk the whole hot feed
  let pageToken: string | undefined;
  do {
    const page = await sdk.trades.getTrades(pair.id, { pageSize: 1000, pageToken });
    handle(page.trades);
    pageToken = page.nextPageToken || undefined;
  } while (pageToken);
  ```
</ResponseField>

<ResponseField name="trades.getTradeById" type="(tradeId: string) => Promise<TradeEvent>">
  Fetch a single trade by its UUID. Throws `APIError` if the trade is not found.

  **Parameters:**

  * `tradeId`: string - Trade UUID

  **Returns:** A single `TradeEvent` (same shape as the entries returned by `getTrades`). `data.executedAt` is an empty string if the trade has no recorded execution time.

  **Example:**

  ```typescript theme={null}
  const trade = await sdk.trades.getTradeById('a1b2c3d4-...');
  console.log(`${trade.data.makerSide} ${trade.data.quantity} @ ${trade.data.price}`);
  ```
</ResponseField>


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