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

# Trading & Orders

> Create and manage orders with the TypeScript SDK

## Prerequisites

Before placing orders, initialize the required clients:

```typescript theme={null}
import { ethers } from 'ethers';
import {
  HttpClient,
  MarketFetcher,
  OrderClient,
} from '@limitless-exchange/sdk';

const httpClient = new HttpClient({
  baseURL: 'https://api.limitless.exchange',
  apiKey: process.env.LIMITLESS_API_KEY,
});

const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!);
const marketFetcher = new MarketFetcher(httpClient);

const orderClient = new OrderClient({
  httpClient,
  wallet,
  marketFetcher, // Optional: enables venue caching for faster order creation
});
```

## OrderClient Constructor

| Option          | Type            | Required | Description                                                                       |
| --------------- | --------------- | -------- | --------------------------------------------------------------------------------- |
| `httpClient`    | `HttpClient`    | Yes      | Authenticated HTTP client                                                         |
| `wallet`        | `ethers.Wallet` | Yes      | Wallet for EIP-712 signing                                                        |
| `marketFetcher` | `MarketFetcher` | No       | Enables automatic venue caching. If omitted, venue data is fetched on each order. |

<Note>
  The `OrderClient` automatically fetches your user profile data (including `ownerId`) from the API on the first order. You do not need to supply it manually.
</Note>

## Token Approvals

Before trading, your wallet must approve the relevant venue contracts to spend USDC and conditional tokens. This is a one-time setup per venue.

### Approval Requirements

| Market Type | USDC Approval               | Conditional Token Approval  |
| ----------- | --------------------------- | --------------------------- |
| CLOB        | Venue `exchange` contract   | Venue `exchange` contract   |
| NegRisk     | NegRisk `exchange` contract | NegRisk `exchange` contract |

### Manual Approval with ethers

```typescript theme={null}
import { ethers } from 'ethers';

const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // USDC on Base
const ERC20_ABI = [
  'function approve(address spender, uint256 amount) returns (bool)',
];
const ERC1155_ABI = [
  'function setApprovalForAll(address operator, bool approved)',
];

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);

async function approveVenue(venueExchange: string) {
  const usdc = new ethers.Contract(USDC_ADDRESS, ERC20_ABI, signer);
  const tx1 = await usdc.approve(venueExchange, ethers.MaxUint256);
  await tx1.wait();
  console.log('USDC approved for', venueExchange);
}
```

<Tip>
  Run approvals once per venue. After approval, all subsequent orders on markets using that venue work without additional on-chain transactions.
</Tip>

## GTC Orders (Good-Til-Cancelled)

GTC orders remain on the orderbook until filled, cancelled, or the market resolves. Specify `price` (probability between 0 and 1) and `size` (number of shares).

### Buy YES shares

```typescript theme={null}
import { OrderType, Side } from '@limitless-exchange/sdk';

const market = await marketFetcher.getMarket('btc-100k-weekly');

const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.yes, // YES token
  side: Side.BUY,
  price: 0.65,
  size: 100,
  orderType: OrderType.GTC,
});

console.log('Order created:', result.order.id);
```

### Post-only GTC order

Use `postOnly: true` to ensure your order is never filled immediately as a taker. If the order would cross the spread (i.e., match against existing orders), it is **rejected** instead. This guarantees you always receive maker fees.

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.yes, // YES token
  side: Side.BUY,
  price: 0.65,
  size: 100,
  orderType: OrderType.GTC,
  postOnly: true,
});
```

### Sell NO shares

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.no, // NO token
  side: Side.SELL,
  price: 0.40,
  size: 50,
  orderType: OrderType.GTC,
});
```

### GTC Parameter Reference

| Parameter    | Type        | Description                                                                              |
| ------------ | ----------- | ---------------------------------------------------------------------------------------- |
| `marketSlug` | `string`    | Market slug identifier                                                                   |
| `tokenId`    | `string`    | YES or NO token ID from `market.tokens` (yes / no)                                       |
| `side`       | `Side`      | `Side.BUY` (0) or `Side.SELL` (1)                                                        |
| `price`      | `number`    | Price between 0 and 1 (exclusive), tick-aligned                                          |
| `size`       | `number`    | Number of shares (positive, step-aligned)                                                |
| `orderType`  | `OrderType` | `OrderType.GTC`                                                                          |
| `postOnly`   | `boolean`   | Optional. When `true`, rejects the order if it would immediately match. Default `false`. |

## Self-Trade Prevention

Pass `stpPolicy` to control what happens when your incoming order would match your own resting order on the same token. It is a top-level request field — not part of the EIP-712 signed order — and applies to any order type. Omit it to keep the server default, `cancel_maker`.

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.positionIds[0], // YES token
  side: Side.BUY,
  price: 0.65,
  size: 100,
  orderType: OrderType.GTC,
  stpPolicy: 'cancel_maker', // 'cancel_maker' | 'cancel_taker' | 'cancel_both'
});
```

| Value          | Result                                                                                 |
| -------------- | -------------------------------------------------------------------------------------- |
| `cancel_maker` | Default. Cancels your conflicting resting order and continues with the incoming order. |
| `cancel_taker` | Rejects the incoming order before it self-trades.                                      |
| `cancel_both`  | Cancels your conflicting resting order and rejects the incoming order.                 |

The create-order response carries an `execution` object with the outcome:

```typescript theme={null}
if (result.execution.settlementStatus === 'CANCELED' &&
    result.execution.reason === 'STP_TAKER_REJECTED') {
  console.log('Order rejected by self-trade prevention');
} else if (result.execution.stpMakerCancels?.length) {
  console.log('Cancelled own resting orders:', result.execution.stpMakerCancels);
}
```

| `execution` field  | Type        | Description                                                         |
| ------------------ | ----------- | ------------------------------------------------------------------- |
| `settlementStatus` | `string`    | `CANCELED` when a `cancel_taker` / `cancel_both` order is rejected. |
| `reason`           | `string?`   | `STP_TAKER_REJECTED` when the incoming order was rejected.          |
| `stpMakerCancels`  | `string[]?` | Resting order ids cancelled by `cancel_maker` / `cancel_both`.      |

<Note>
  Self-trade prevention blocks same-profile matches on the **same token** only. Orders on a different token of the same profile are unaffected. The wire field is always `stpPolicy`, regardless of the SDK.
</Note>

## FAK Orders (Fill-And-Kill)

FAK orders use the same `price` and `size` inputs as GTC, but they only consume immediately available liquidity and cancel any unmatched remainder.

`postOnly` is not supported for FAK orders.

### Buy with FAK

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.yes, // YES token
  side: Side.BUY,
  price: 0.45,
  size: 100,
  orderType: OrderType.FAK,
});

if (result.makerMatches.length > 0) {
  console.log(`FAK order matched immediately with ${result.makerMatches.length} fill(s)`);
} else {
  console.log('FAK remainder was cancelled.');
}
```

### Sell with FAK

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.no, // NO token
  side: Side.SELL,
  price: 0.40,
  size: 50,
  orderType: OrderType.FAK,
});
```

### FAK Parameter Reference

| Parameter    | Type        | Description                                        |
| ------------ | ----------- | -------------------------------------------------- |
| `marketSlug` | `string`    | Market slug identifier                             |
| `tokenId`    | `string`    | YES or NO token ID from `market.tokens` (yes / no) |
| `side`       | `Side`      | `Side.BUY` (0) or `Side.SELL` (1)                  |
| `price`      | `number`    | Price between 0 and 1 (exclusive), tick-aligned    |
| `size`       | `number`    | Number of shares (positive, step-aligned)          |
| `orderType`  | `OrderType` | `OrderType.FAK`                                    |

## FOK Orders (Fill-Or-Kill)

FOK orders execute immediately against the existing orderbook or are rejected entirely. Instead of `price` and `size`, specify `makerAmount` which represents the total value to trade.

### Buy with FOK

For a BUY FOK order, `makerAmount` is the amount of USDC you are willing to spend:

```typescript theme={null}
import { OrderType, Side } from '@limitless-exchange/sdk';

const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.yes, // YES token
  side: Side.BUY,
  makerAmount: 50, // Spend up to 50 USDC
  orderType: OrderType.FOK,
});
```

### Sell with FOK

For a SELL FOK order, `makerAmount` is the number of shares you want to sell:

```typescript theme={null}
const result = await orderClient.createOrder({
  marketSlug: market.slug,
  tokenId: market.tokens.yes, // YES token
  side: Side.SELL,
  makerAmount: 100, // Sell 100 shares
  orderType: OrderType.FOK,
});
```

### FOK Parameter Reference

| Parameter     | Type        | Description                                               |
| ------------- | ----------- | --------------------------------------------------------- |
| `marketSlug`  | `string`    | Market slug identifier                                    |
| `tokenId`     | `string`    | YES or NO token ID from `market.tokens` (yes / no)        |
| `side`        | `Side`      | `Side.BUY` (0) or `Side.SELL` (1)                         |
| `makerAmount` | `number`    | BUY: USDC to spend. SELL: shares to sell. Max 6 decimals. |
| `orderType`   | `OrderType` | `OrderType.FOK`                                           |

## NegRisk Orders

When trading NegRisk markets, always use the **submarket slug** -- not the group slug. Fetch the group to discover submarkets, then fetch the specific submarket to get its token IDs.

```typescript theme={null}
import { OrderType, Side } from '@limitless-exchange/sdk';

// 1. Fetch the NegRisk group
const group = await marketFetcher.getMarket('us-election-2024');

// 2. Pick a submarket from the group's markets array
const submarket = group.markets[0];

// 3. Fetch submarket details to get token IDs
const submarketDetail = await marketFetcher.getMarket(submarket.slug);

// 4. Place an order using the submarket slug and token IDs
const result = await orderClient.createOrder({
  marketSlug: submarketDetail.slug,
  tokenId: submarketDetail.tokens.yes, // YES token
  side: Side.BUY,
  price: 0.55,
  size: 25,
  orderType: OrderType.GTC,
});
```

<Warning>
  Passing the group slug to `createOrder()` will fail. The group slug does not resolve to a tradeable market. Always use the submarket slug.
</Warning>

## Cancel Orders

Cancel an open order by its ID:

```typescript theme={null}
const result = await orderClient.cancel('order_abc123');
console.log(result.message);
```

Cancel all orders in a market:

```typescript theme={null}
const result = await orderClient.cancelAll('btc-100k-weekly');
console.log(result.message);
```

## Order States

| State              | Description                                               |
| ------------------ | --------------------------------------------------------- |
| `OPEN`             | Order is live on the orderbook                            |
| `PARTIALLY_FILLED` | Some shares have been matched                             |
| `FILLED`           | Order fully matched                                       |
| `CANCELLED`        | Order cancelled by user or system                         |
| `REJECTED`         | Order rejected (insufficient funds, invalid params, etc.) |

## Error Handling

The SDK throws `APIError` for HTTP-level failures. Inspect `status` and `message` for details.

```typescript theme={null}
import { APIError, OrderType, Side } from '@limitless-exchange/sdk';

try {
  await orderClient.createOrder({
    marketSlug: 'btc-100k-weekly',
    tokenId: market.tokens.yes,
    side: Side.BUY,
    price: 0.65,
    size: 100,
    orderType: OrderType.GTC,
  });
} catch (error) {
  if (error instanceof APIError) {
    switch (error.status) {
      case 400:
        console.error('Bad request:', error.message);
        break;
      case 401:
        console.error('Unauthorized: check your API key');
        break;
      case 429:
        console.error('Rate limited: slow down requests');
        break;
      default:
        console.error(`API error ${error.status}:`, error.message);
    }
  } else {
    throw error;
  }
}
```

See the [Error Handling and Retry](/developers/sdk/typescript/error-handling) guide for retry strategies.

## Validation Rules

<Accordion title="Price validation">
  Prices must be between 0 and 1 (exclusive). A price of `0.65` means you value the outcome at 65%. Prices outside this range will be rejected by the API.

  ```typescript theme={null}
  // Valid
  price: 0.01
  price: 0.99

  // Invalid (will throw)
  price: 0
  price: 1
  price: 1.5
  price: -0.1
  ```
</Accordion>

<Accordion title="Size validation">
  Size must be a positive number. Fractional sizes are allowed if they align to the shares step (0.001). Max 3 decimal places.

  ```typescript theme={null}
  // Valid
  size: 1
  size: 100
  size: 1.5
  size: 0.001

  // Invalid (will throw)
  size: 0
  size: -1
  size: 0.0001  // too many decimals
  ```
</Accordion>
