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

# useTrade

> Place and manage orders, including perp orders and conditional TP/SL

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

const {
  placeLimitOrder,
  placeMarketOrder,
  cancelOrder,
  batchCancel,
  batchCancelAll,
  replaceOrder,
  batchCreate,
  batchReplace,
  cancelConditionalOrder,
  listConditionalOrders,
  getPaginatedOrders,
  getOrder,
} = useTrade();
```

The hook returns the same surface as [`sdk.trading`](/sdk/typescript/trades).

## Place Orders

<ResponseField name="placeLimitOrder" type="(pairId, side, quantity, price, options?) => Promise<CreateOrderResponse>">
  Place a limit order. Spot by default; opt into perp with `tradingMode: "MARGIN"` and `leverage`.

  **`options`:**

  * `tradingMode?`: `"SPOT" | "MARGIN"`
  * `useMasterBalance?`: boolean
  * `expirationDate?`: string — ISO 8601; pair with `GTC` for an order that expires at a set time
  * `timeInForce?`: `"GTC" | "IOC" | "FOK"` (`GTD` is declared in the protos but never implemented — the server rejects it)
  * `postOnly?`: boolean — maker-only; the order is rejected instead of matching if it would cross the book. `GTC` only — rejected on `MARKET` orders and with `IOC`/`FOK`. See [Post-Only Orders](/developers/trade#post-only-orders)
  * `selfTradePreventionMode?`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — how the engine resolves this order meeting one of your own resting orders (your whole wallet family). Overrides the wallet default; omit to inherit that, or the platform default `CANCEL_MAKER`. See [Self-Trade Prevention](/developers/trade#self-trade-prevention)
  * `leverage?`: string — decimal string; required for normal margin orders, omit only for reduce-only
  * `reduceOnly?`: boolean — order can only shrink existing exposure
  * `marginAccountId?`: string — optional legacy override; omit for normal perp orders
  * `positionSide?`: `"LONG" | "SHORT" | "NONE"` — deprecated. `side` is the authoritative direction; if supplied, `positionSide` must agree (`BUY`↔`LONG`, `SELL`↔`SHORT`)
  * `clientOrderId?`: string — correlation handle echoed on order reads and `orders` WebSocket events (≤64 chars from `A-Za-z0-9._:-`); not an idempotency key. Restate it on `replaceOrder` — a replacement inherits nothing. See [Client Order IDs](/developers/trade#client-order-ids)
  * `idempotencyKey?`: string — durable submission key. The SDK generates one for this call when omitted; retain and reuse an explicit key with the same payload to recover across calls or restarts within 24 hours. See [Safe Retries](/developers/trade#safe-retries-with-idempotency-keys)

  Perps are one-way — `side` sets the direction. Attach TP/SL at entry with `takeProfit`/`stopLoss` (margin entry orders only; legs activate when the order fills), or attach to the open position with [`usePositions().attachPositionTpSl`](/sdk/react/hooks/use-positions). A `trailingStop` leg attaches the same way, arms only when the order fully fills, and returns `trailingStopOrderId` only when the trailing stop materializes (a full fill on arrival or a resting remainder; a partial `IOC` / `MARKET` fill whose remainder is cancelled creates none) — see [Trailing Stop](/developers/trade#trailing-stop).

  **Perp example:**

  ```tsx theme={null}
  const { placeLimitOrder } = useTrade();
  const { attachPositionTpSl } = usePositions();

  // Open a 10x long with TP/SL attached at entry — the legs activate when
  // the order fills and cancel if it is cancelled or expires unfilled.
  await placeLimitOrder(tradingPairId, "BUY", "1.5", "2000", {
    tradingMode: "MARGIN",
    leverage: "10",
    takeProfit: { triggerPrice: "2200", orderType: "MARKET" },
    stopLoss: { triggerPrice: "1900", orderType: "LIMIT", limitPrice: "1895" },
  });

  // Or attach / adjust TP/SL after the position opens
  await attachPositionTpSl(positionId, {
    takeProfit: { triggerPrice: "2200", orderType: "MARKET" },
    stopLoss: { triggerPrice: "1900", orderType: "LIMIT", limitPrice: "1895" },
  });
  ```
</ResponseField>

<ResponseField name="placeMarketOrder" type="(pairId, side, quantity, options?) => Promise<CreateOrderResponse>">
  Place a market order. Market orders execute inside a mandatory server-side price band, 1,000 bps by default (see [Market-order price protection](/trading-mechanics/spot#market-order-price-protection); a perp market can set its own) and can partially fill with the remainder cancelled, or be rejected when no liquidity sits inside the band.

  **`options`:**

  * `tradingMode?`: `"SPOT" | "MARGIN"`
  * `slippageTolerance?`: number — tightens the market's price band (e.g. `0.01` for 1%); cannot widen it
  * `leverage?`: string — required for normal margin orders, omit only for reduce-only
  * `reduceOnly?`: boolean
  * `marginAccountId?`: string — optional legacy override; omit for normal perp orders
  * `positionSide?`: `"LONG" | "SHORT" | "NONE"` — deprecated; `side` is authoritative
  * `clientOrderId?`: string — correlation handle echoed on order reads and `orders` WebSocket events (≤64 chars from `A-Za-z0-9._:-`); not an idempotency key. See [Client Order IDs](/developers/trade#client-order-ids)
  * `selfTradePreventionMode?`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — how the engine resolves this order meeting one of your own resting orders (your whole wallet family). Overrides the wallet default; omit to inherit that, or the platform default `CANCEL_MAKER`. See [Self-Trade Prevention](/developers/trade#self-trade-prevention)
  * `idempotencyKey?`: string — durable submission key. The SDK generates one for this call when omitted; retain and reuse an explicit key with the same payload to recover across calls or restarts within 24 hours. See [Safe Retries](/developers/trade#safe-retries-with-idempotency-keys)
</ResponseField>

## Order Management

<ResponseField name="cancelOrder" type="(orderId) => Promise<CancelOrderResponse>">
  Cancel an existing order
</ResponseField>

<ResponseField name="batchCancel" type="(orderIds: string[]) => Promise<BatchCancelOrdersResponse>">
  Cancel specific orders by their IDs.

  **Returns:**

  * `totalRequested`: number
  * `totalCancelled`: number
  * `totalFailed`: number
  * `results`: `BatchCancelResult[]` — each with `orderId`, optional `cancelledAt`, optional `error: { code, message }`

  Use `totalFailed === 0` for an overall-success check.

  Capped at **100 order IDs** per request by the API (enforced server-side, no client-side schema check); a longer list is rejected with `400 At most 100 orders per batch request`. `batchCancelAll` enumerates active orders server-side, with a separate cap of **20,000 matching active orders**.

  **Example:**

  ```tsx theme={null}
  const { batchCancel } = useTrade();

  async function handleCancelSelected(orderIds: string[]) {
    const result = await batchCancel(orderIds);
    console.log(`Cancelled ${result.totalCancelled} of ${result.totalRequested}`);
    result.results.forEach((r) => {
      if (r.error) console.error(`Order ${r.orderId} failed: ${r.error.message}`);
    });
  }
  ```
</ResponseField>

<ResponseField name="batchCancelAll" type="(tradingPairId?: string) => Promise<BatchCancelOrdersResponse>">
  Cancel all active orders, optionally scoped to one trading pair.

  The API caps the request at **20,000 matching active orders**. Above that cap, it cancels nothing and returns REST `400`; narrow by trading pair or cancel explicit batches of at most 100 IDs. The cap check is all-or-nothing and runs before any cancellation.

  Returns the same shape as `batchCancel`.

  **Throws:**

  * REST `400` when the pair UUID is invalid or more than 20,000 matching active orders would be cancelled
  * REST `401` when authentication is missing or invalid
  * REST `403` when a delegated session omits the required pair scope or lacks permission
  * REST `500` for an internal matching-engine failure
  * REST `503` for a transient matching-engine or transport failure

  **Example:**

  ```tsx theme={null}
  const { batchCancelAll } = useTrade();

  async function handleCancelAll() {
    const result = await batchCancelAll();
    alert(`Cancelled ${result.totalCancelled} orders`);
  }
  ```
</ResponseField>

<ResponseField name="replaceOrder" type="(orderId, newOrder) => Promise<ReplaceOrderResponse>">
  Modify an existing order's price or quantity. `quantity` is the order's new **total** (a partially filled order rests `total - filled` and must exceed the filled amount; omit it to keep the total), and the replacement inherits the original's fill history — see [`replaceOrder`](/sdk/typescript/trades) for the full semantics. `newOrder` also accepts `postOnly`, `clientOrderId` and `selfTradePreventionMode` — a replacement is a new order and inherits none of them, so restate `clientOrderId` to keep the handle and `selfTradePreventionMode` to keep the override. `batchReplace` items take the same three. See [Client Order IDs](/developers/trade#client-order-ids).
</ResponseField>

## Batch Operations

<ResponseField name="batchCreate" type="(orders: BatchCreateOrderParams[]) => Promise<BatchCreateOrdersResponse>">
  Create multiple orders in a single request. Capped at **100 items** — a longer array is rejected client-side (`At most 100 orders per batch request`, exported as `MAX_BATCH_ORDER_ITEMS`) before the request is sent.

  **`BatchCreateOrderParams`:**

  * `tradingPairId`: string
  * `orderType`: `"LIMIT" | "MARKET"`
  * `side`: `"BUY" | "SELL"`
  * `quantity`: string
  * `price?`: string — required for LIMIT
  * `timeInForce?`: `"GTC" | "IOC" | "FOK"` — LIMIT items only; the SDK rejects it on a MARKET item (market orders take no time-in-force and always execute IOC-style inside the price band)
  * `postOnly?`: boolean — maker-only; the item is rejected instead of matching if it would cross the book, reported per-item as `error.code === "POST_ONLY_WOULD_CROSS"`. LIMIT items with `GTC` only. See [Post-Only Orders](/developers/trade#post-only-orders)
  * `selfTradePreventionMode?`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — per-item self-trade-prevention override. The value is validated client-side, so an unknown mode throws `ValidationError` before the request is sent; the per-item `error.code` `INVALID_SELF_TRADE_PREVENTION_MODE` is the raw REST/gRPC caller's shape. See [Self-Trade Prevention](/developers/trade#self-trade-prevention)
  * `slippageTolerance?`: number — for MARKET
  * `expirationDate?`: string — ISO 8601, custom expiry for GTC
  * `useMasterBalance?`: boolean
  * `clientOrderId?`: string — per-item correlation handle echoed on order reads and `orders` WebSocket events (≤64 chars from `A-Za-z0-9._:-`); a malformed value fails that item with `error.code === "INVALID_CLIENT_ORDER_ID"`. Not an idempotency key. See [Client Order IDs](/developers/trade#client-order-ids)
  * `idempotencyKey?`: string — per-item durable submission key. The SDK generates one per item when omitted. Retain explicit per-item keys for recovery across calls; each key protects only its own item and does not make the batch atomic. See [Safe Retries](/developers/trade#safe-retries-with-idempotency-keys)

  **Returns — `BatchCreateOrdersResponse`:**

  * `totalRequested`: number
  * `totalSucceeded`: number
  * `totalFailed`: number
  * `results`: `BatchCreateResult[]`

  **`BatchCreateResult`:**

  * `orderId`: string — empty if creation failed before ID assignment
  * `matchResult?`: `MatchResult` — present if order was immediately matched
  * `error?`: `BatchError` — `{ code, message }`. Treat absence of `error` as success.

  **Example:**

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

  function TradingPanel({ pairId }: { pairId: string }) {
    const { batchCreate } = useTrade();

    async function placeBracketOrders() {
      const result = await batchCreate([
        { tradingPairId: pairId, orderType: "LIMIT", side: "BUY", quantity: "1.0", price: "2000.00" },
        { tradingPairId: pairId, orderType: "LIMIT", side: "BUY", quantity: "0.5", price: "1950.00" },
      ]);

      result.results.forEach((r) => {
        if (r.error) {
          console.error(`Failed: ${r.error.code} — ${r.error.message}`);
        } else {
          console.log(`Order ${r.orderId} created`);
        }
      });
    }
  }
  ```
</ResponseField>

<ResponseField name="batchReplace" type="(orders: BatchReplaceOrderParams[]) => Promise<BatchReplaceOrdersResponse>">
  Replace multiple orders in a single request. Each replacement atomically cancels the original and creates a new one. Capped at **100 items** — a longer array is rejected client-side (`At most 100 orders per batch request`) before the request is sent.

  **`BatchReplaceOrderParams`:**

  * `orderId`: string
  * `price?`: string
  * `quantity?`: string — the order's new **total** (same rule as `replaceOrder`): a partially filled order rests `total - filled` and the total must exceed the filled amount; omit it to keep the order's total. Margin reduce-only orders follow the same rule, and the replacement inherits the original's fill history
  * `useMasterBalance?`: boolean
  * `clientOrderId?`: string — per-item correlation handle for the replacement order (a replacement does not carry over the original's `clientOrderId`, so restate it). A malformed handle fails **its item alone** (`error.code` `INVALID_CLIENT_ORDER_ID`, the same code batch-create uses) with its original untouched. See [Client Order IDs](/developers/trade#client-order-ids)
  * `postOnly?`: boolean — maker-only intent for the replacement; never inherited from the original. See [Post-Only Orders](/developers/trade#post-only-orders)
  * `selfTradePreventionMode?`: `"CANCEL_MAKER" | "CANCEL_TAKER" | "CANCEL_BOTH" | "SKIP"` — per-item self-trade-prevention override for the replacement, likewise never inherited; omit it to fall back to your wallet default. An unknown value is rejected client-side with `ValidationError` before sending; `error.code` `INVALID_SELF_TRADE_PREVENTION_MODE` is what a raw REST/gRPC caller gets on that item alone. See [Self-Trade Prevention](/developers/trade#self-trade-prevention)

  Every item must still change something: `BatchReplaceOrderItemSchema` requires **at least one of `price` or `quantity`**, so an item carrying only `selfTradePreventionMode` (or only `postOnly`) throws `ValidationError` before the request is sent. Restate the price or quantity alongside the option.

  **Returns — `BatchReplaceOrdersResponse`:**

  * `totalRequested`, `totalSucceeded`, `totalFailed`: number
  * `results`: `BatchReplaceResult[]`

  **`BatchReplaceResult`:**

  * `originalOrderId`: string
  * `newOrderId?`: string
  * `updatedFields?`: `{ price?, quantity? }`
  * `matchResult?`: `MatchResult`
  * `error?`: `BatchError` — `{ code, message }`. Absence of `error` indicates success.

  **Example:**

  ```tsx theme={null}
  const { batchReplace } = useTrade();

  async function adjustPrices(orders: { id: string; newPrice: string }[]) {
    const result = await batchReplace(
      orders.map(({ id, newPrice }) => ({ orderId: id, price: newPrice })),
    );

    result.results.forEach((r) => {
      if (r.error) {
        console.error(`Failed: ${r.error.code} — ${r.error.message}`);
      } else {
        console.log(`New order ID: ${r.newOrderId} (replaced ${r.originalOrderId})`);
      }
    });
  }
  ```
</ResponseField>

## Conditional Orders (Perp TP/SL)

Attach TP/SL and trailing stops at entry via `takeProfit`/`stopLoss`/`trailingStop` on `placeLimitOrder` / `placeMarketOrder`, or to an open position with [`usePositions().attachPositionTpSl`](/sdk/react/hooks/use-positions). The hook below covers listing and cancelling conditional orders. Concepts and lifecycle: [Order Management](/developers/trade#conditional-perps-orders).

<ResponseField name="cancelConditionalOrder" type="(conditionalOrderId: string) => Promise<CancelConditionalOrderResponse>">
  Cancel an active conditional order.
</ResponseField>

<ResponseField name="listConditionalOrders" type="(params?: ListConditionalOrdersParams) => Promise<ListConditionalOrdersResponse>">
  List conditional orders for the authenticated user. Passes `params` straight to `sdk.trading.listConditionalOrders`, so cursor (`pageToken`) pagination is the default: follow each response's `nextPageToken` until it comes back empty.

  **Parameters:**

  * `marginAccountId?`, `tradingPairId?`, `state?`, `pageToken?`, `pageSize?` (up to `1000`)
</ResponseField>

## Order Queries

<ResponseField name="getPaginatedOrders" type="(params?) => Promise<GetPaginatedOrdersResponse>">
  Fetch paginated orders
</ResponseField>

<ResponseField name="getOrder" type="(orderId) => Promise<GetOrderResponse>">
  Get order details by ID
</ResponseField>


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