> For the complete documentation index, see [llms.txt](https://dev.strixlab.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.strixlab.io/typescript-sdk/orders.md).

# Orders

Limit orders, market orders, batches, and the cancel kill-switch — everything that puts risk on or takes it off.

Everything on this page needs [HMAC credentials](/http-and-websocket-api/authentication.md). Without them the SDK throws `StrixAuthError` before making a request.

{% hint style="warning" %}
**Fills are asynchronous.** The response to a placement is an acknowledgement — a crossing order still returns `status: "OPEN"` with `filledQuantity: "0"`. Watch [`order.update` / `trade.settled`](/typescript-sdk/websockets.md#your-orders-and-fills) for what actually happened. See [Core Concepts](/get-started/concepts.md#orders-settle-asynchronously).
{% endhint %}

## Limit orders

```ts
const order = await client.orders.place({
  tokenId,
  side:      'BUY',      // 'BUY' | 'SELL'
  price:     0.65,       // 65¢ — must be a multiple of the market's tickSize
  quantity:  100,        // shares
  orderType: 'GTC',      // 'GTC' | 'GTD' | 'FOK' | 'FAK'
});

order.orderHash;   // ← keep this; it is the handle for cancelling
order.status;      // "OPEN"
```

### Order types

<table><thead><tr><th width="110">Type</th><th width="230">Name</th><th>Behaviour</th></tr></thead><tbody><tr><td><code>GTC</code></td><td>Good Till Cancelled <em>(default)</em></td><td>Rests on the book until you cancel it or the market closes.</td></tr><tr><td><code>GTD</code></td><td>Good Till Date</td><td>Rests until <code>expiredAt</code>, then auto-cancels.</td></tr><tr><td><code>FOK</code></td><td>Fill Or Kill</td><td>Must fill <strong>completely</strong> at the limit right now, or it is cancelled entirely.</td></tr><tr><td><code>FAK</code></td><td>Fill And Kill <em>(IOC)</em></td><td>Fills whatever is available right now; the remainder is cancelled.</td></tr></tbody></table>

{% tabs %}
{% tab title="GTC" %}

```ts
// Rest a bid and wait to be hit. Makers pay no fee.
await client.orders.place({
  tokenId, side: 'BUY', price: 0.60, quantity: 250, orderType: 'GTC',
});
```

{% endtab %}

{% tab title="GTD" %}

```ts
// Auto-cancels in an hour — good for quotes you might forget about.
await client.orders.place({
  tokenId, side: 'SELL', price: 0.70, quantity: 50,
  orderType: 'GTD',
  expiredAt: new Date(Date.now() + 3_600_000).toISOString(),
});
```

`expiredAt` must be **at least one minute** in the future — the expiry sweep runs every 60 seconds, so anything shorter could match after its nominal expiry. TypeScript enforces that `expiredAt` is present on `GTD` and absent on every other type.
{% endtab %}

{% tab title="FOK" %}

```ts
// All 100 shares at 66¢ or better, or nothing at all.
await client.orders.place({
  tokenId, side: 'BUY', price: 0.66, quantity: 100, orderType: 'FOK',
});
```

{% endtab %}

{% tab title="FAK" %}

```ts
// Take whatever is resting at the touch, cancel the rest.
// This is the tick-safe way to "sweep" a book.
await client.orders.place({
  tokenId, side: 'SELL', price: bestBid, quantity: 100, orderType: 'FAK',
});
```

{% endtab %}
{% endtabs %}

### Validation rules

Break one of these and you get a `400` before the order ever reaches the book:

<table><thead><tr><th width="240">Rule</th><th>Detail</th></tr></thead><tbody><tr><td>Price grid</td><td>A multiple of the event's <code>tickSize</code> (<code>0.01</code> or <code>0.001</code>), and of <code>0.0001</code> overall.</td></tr><tr><td>Price bounds</td><td><code>0.0001 ≤ price ≤ 0.9999</code>. Exactly 0 or 1 is rejected.</td></tr><tr><td>Minimum value</td><td><code>price × quantity ≥ $1.00</code>.</td></tr><tr><td>Market state</td><td>The market must be open and have <code>enableOrderBook: true</code>.</td></tr><tr><td>Balance</td><td>A buy holds <code>price × quantity</code> USDC; a sell holds the shares. Both are reserved until the order is filled or cancelled.</td></tr><tr><td>Open orders</td><td>Up to <strong>2,000</strong> open orders per API-key account.</td></tr></tbody></table>

{% hint style="info" %}
**Self-trading is rejected.** A buy that would cross your own resting sell (or the equivalent on the sibling outcome, in YES terms) is refused with a message naming the order it hit. Cancel first, then re-quote — or use [`cancelBatch`](#cancelling) in the same cycle.
{% endhint %}

## Market orders

A market order sweeps the book instead of naming a price. **Sizing differs by side, because the units differ:** a buy is capped by the USDC it may spend, a sell by the shares it sells.

{% tabs %}
{% tab title="Market BUY" %}

```ts
const { order, executionPlan } = await client.orders.placeMarket({
  tokenId,
  side:     'BUY',
  maxSpend: 100,          // USDC — never spends more than this
});

executionPlan.submittedQuantity;        // "153.846154" shares
executionPlan.limitPrice;               // "0.66" — worst level the sweep reaches
executionPlan.estimatedAveragePrice;    // "0.6534"
executionPlan.estimatedQuoteAmount;     // "99.998..."
```

{% endtab %}

{% tab title="Market SELL" %}

```ts
const { order, executionPlan } = await client.orders.placeMarket({
  tokenId,
  side:           'SELL',
  quantity:       50,        // shares
  timeInForce:    'FAK',     // fill what's there, cancel the rest
  maxSlippageBps: 50,        // optional: abort if the sweep is worse than 0.5%
});

// If your wallet held fewer shares than requested, the order is trimmed and
// executionPlan.availableQuantity tells you what was actually submitted.
```

{% endtab %}
{% endtabs %}

`timeInForce` defaults to **`FOK`** — all or nothing. Pass `'FAK'` to accept a partial sweep.

{% hint style="info" %}
`executionPlan` is a **plan, not a fill report**. Every field is a decimal string (these are money and share counts; floats would round them). The fills arrive over the WebSocket.
{% endhint %}

### Two behaviours that surprise people

{% hint style="danger" %}
**1. A market BUY only takes resting asks on the same token.** It does not mint-cross into the sibling outcome's bids. On a book with bids but no asks it fails with `no_liquidity`, even though the screen shows plenty of size. To lift a resting bid on the other leg, place a **crossing limit buy on the sibling token at `1 − bidPrice`**.

**2. A market SELL's computed price can land off-tick** and be rejected on a coarse-tick market. Prefer a tick-aligned crossing limit sell at the best bid with `orderType: 'FAK'` — same effect, no rounding surprise.
{% endhint %}

```ts
// The reliable "take the bid" pattern on a 0.01-tick market:
const { price: bestBid } = await client.orderbook.price(tokenId, 'sell');
await client.orders.place({ tokenId, side: 'SELL', price: bestBid, quantity: 50, orderType: 'FAK' });
```

## Batch orders

Up to **15** orders in one round-trip. Each is validated independently — one failure does not roll back the rest.

{% code title="Quote both sides at once" %}

```ts
const results = await client.orders.placeBatch([
  { tokenId: yesTokenId, side: 'BUY',  price: 0.60, quantity: 100, orderType: 'GTC' },
  { tokenId: yesTokenId, side: 'SELL', price: 0.64, quantity: 100, orderType: 'GTC' },
]);

for (const r of results) {
  if (r.success) console.log(`#${r.index} placed ${r.order.orderHash}`);
  else           console.error(`#${r.index} rejected: ${r.error}`);
}
```

{% endcode %}

`index` maps each result back to the order you submitted, in the order you submitted them.

## Cancelling

<table><thead><tr><th width="290">Call</th><th width="130">Rate limited</th><th>Use for</th></tr></thead><tbody><tr><td><code>orders.cancel(orderHash)</code></td><td><strong>Yes</strong></td><td>A single order, off the hot path.</td></tr><tr><td><code>orders.cancelBatch(hashes)</code></td><td>No</td><td>Up to 100 specific orders — the requote path.</td></tr><tr><td><code>orders.cancelAll({ … })</code></td><td>No</td><td>Everything, or one token / one market. The kill-switch.</td></tr></tbody></table>

```ts
await client.orders.cancel(order.orderHash);

const { cancelled, failed } = await client.orders.cancelBatch([h1, h2, h3]);

await client.orders.cancelAll();                     // everything
await client.orders.cancelAll({ tokenId });          // one outcome
await client.orders.cancelAll({ marketId });         // one market
```

{% hint style="danger" %}
**`cancelAll()` is the most important call in your bot.** It is deliberately exempt from rate limiting, because cancels only ever remove risk. On a disconnect, a model failure, an unhandled exception, or shutdown — fire it first, ask questions later.
{% endhint %}

Three things to know about cancel results:

1. **Single cancel&#x20;*****is*****&#x20;rate limited.** It shares the order-write budget with placements, so it can return `429` under load. Prefer the bulk paths in a loop.
2. **A failed cancel means the order is still live.** Never assume it's gone — quoting against an assumed-dead order is how you self-trade.
3. **Bulk results are counts, not lists.** Orders that were already filled, cancelled, or locked in settlement (`HOLD`) are skipped and counted in `failed`. Re-list your open orders next cycle to pick them up once settlement confirms.

{% hint style="info" %}
Only `OPEN` orders can be cancelled. Once any part of an order is matched, cancelling would race the in-flight settlement, so the venue refuses — that quantity is committed.
{% endhint %}

## Listing your open orders

```ts
const open = await client.orders.list();                          // all of them
const forMarket = await client.orders.list({ marketId });         // scoped
```

Returns your `OPEN` and `PARTIALLY_FILLED` orders. The response is always scoped to the caller — for aggregated market depth use the [public order book](/typescript-sdk/orderbook.md).

{% code title="What's actually still resting" %}

```ts
for (const o of await client.orders.list()) {
  const resting = Number(o.quantity) - Number(o.filledQuantity) - Number(o.holdQuantity);
  console.log(`${o.side} ${resting}/${o.quantity} @ ${o.price}  ${o.status}`);
}
```

{% endcode %}

{% hint style="warning" %}
Subtract **`holdQuantity`** as well as `filledQuantity`. The two-term version looks correct and overstates your resting size for the \~15–20 seconds a settlement is in flight.
{% endhint %}

## The Order object

{% code title="Every field is a decimal string" %}

```ts
interface Order {
  id: string;
  orderHash: string;          // cancel handle
  walletAddress: string;
  marketId: string;
  tokenId: string;
  side: 'BUY' | 'SELL';
  price: string;              // "0.65"
  quantity: string;           // "100"
  filledQuantity: string;     // settled
  holdQuantity: string;       // matched, settlement in flight
  status: OrderStatus;
  timeInForce: 'GTC' | 'GTD' | 'FOK' | 'FAK';
  expiredAt: string | null;
  createdAt: string;
  updatedAt: string;
}
```

{% endcode %}

Prices and quantities come back as strings on purpose — they are exact decimals on the wire, and parsing them as floats is how rounding bugs get into money. Convert at the edge, keep the string as your record.
