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

## Overview

The `OrderClient` handles order creation, EIP-712 signing, and order management. It supports Good-til-cancelled (GTC), Fill-and-kill (FAK), and Fill-or-kill (FOK) orders.

## Prerequisites

Before placing orders, you need four components:

```python theme={null}
import asyncio
import os
from eth_account import Account
from limitless_sdk.api import HttpClient
from limitless_sdk import HMACCredentials
from limitless_sdk.markets import MarketFetcher
from limitless_sdk.orders import OrderClient
from limitless_sdk.types import Side, OrderType

# Authenticate with a scoped API token (token ID + secret)
http_client = HttpClient(
    hmac_credentials=HMACCredentials(
        token_id=os.environ["LMTS_TOKEN_ID"],
        secret=os.environ["LMTS_TOKEN_SECRET"],
    ),
)
account = Account.from_key("0xYOUR_PRIVATE_KEY")
market_fetcher = MarketFetcher(http_client)
order_client = OrderClient(http_client, account)
```

| Component       | Purpose                                        |
| --------------- | ---------------------------------------------- |
| `HttpClient`    | Authenticated HTTP client for API requests     |
| `Account`       | eth-account wallet for EIP-712 order signing   |
| `MarketFetcher` | Fetches market data and caches venue addresses |
| `OrderClient`   | Creates, signs, and submits orders             |

<Note>
  The `OrderClient` constructor automatically fetches your user profile data (`userData`) from the API. This is used to populate the `ownerId` field on submitted orders.
</Note>

<Warning>
  **Wallet-mode preflight.** Accepting the one-time "choose your trading wallet" prompt in the app enables 1-click (smart wallet) trading on your Limitless profile. Once that mode is set, self-signed orders are rejected with `Signer does not match - you should use embedded address for smart wallet`. Switch the profile to EOA trading mode first: see [Trading wallet mode](/developers/eip712-signing#trading-wallet-mode-whose-address-signs).
</Warning>

## Token approvals

Before your first trade on a given venue, you must approve the exchange contracts to spend your tokens. This is a **one-time on-chain setup** per venue.

<Tabs>
  <Tab title="Standard CLOB">
    For standard CLOB markets, approve USDC and Conditional Tokens to the **exchange** contract:

    ```python theme={null}
    from web3 import Web3

    w3 = Web3(Web3.HTTPProvider("https://mainnet.base.org"))

    USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
    CT_ADDRESS = "0xC9c98965297Bc527861c898329Ee280632B76e18"  # Conditional Token framework address

    market = await market_fetcher.get_market("your-market-slug")
    exchange = market.venue.exchange

    # Approve USDC for BUY orders
    usdc_contract = w3.eth.contract(
        address=Web3.to_checksum_address(USDC_ADDRESS),
        abi=[{
            "name": "approve",
            "type": "function",
            "inputs": [
                {"name": "spender", "type": "address"},
                {"name": "amount", "type": "uint256"},
            ],
            "outputs": [{"name": "", "type": "bool"}],
        }],
    )
    tx = usdc_contract.functions.approve(
        Web3.to_checksum_address(exchange),
        2**256 - 1,  # max approval
    ).build_transaction({
        "from": account.address,
        "nonce": w3.eth.get_transaction_count(account.address),
    })
    signed = w3.eth.account.sign_transaction(tx, account.key)
    w3.eth.send_raw_transaction(signed.raw_transaction)

    # Approve Conditional Tokens for SELL orders
    ct_contract = w3.eth.contract(
        address=Web3.to_checksum_address(CT_ADDRESS),
        abi=[{
            "name": "setApprovalForAll",
            "type": "function",
            "inputs": [
                {"name": "operator", "type": "address"},
                {"name": "approved", "type": "bool"},
            ],
            "outputs": [],
        }],
    )
    tx = ct_contract.functions.setApprovalForAll(
        Web3.to_checksum_address(exchange),
        True,
    ).build_transaction({
        "from": account.address,
        "nonce": w3.eth.get_transaction_count(account.address),
    })
    signed = w3.eth.account.sign_transaction(tx, account.key)
    w3.eth.send_raw_transaction(signed.raw_transaction)
    ```
  </Tab>

  <Tab title="NegRisk">
    For NegRisk markets, you must additionally approve the **adapter** contract:

    ```python theme={null}
    market = await market_fetcher.get_market("your-negrisk-slug")
    exchange = market.venue.exchange
    adapter = market.venue.adapter

    # 1. Approve USDC to the exchange (same as CLOB)
    # ... (see Standard CLOB tab)

    # 2. Approve Conditional Tokens to the exchange
    # ... (see Standard CLOB tab)

    # 3. Approve Conditional Tokens to the adapter (NegRisk only)
    tx = ct_contract.functions.setApprovalForAll(
        Web3.to_checksum_address(adapter),
        True,
    ).build_transaction({
        "from": account.address,
        "nonce": w3.eth.get_transaction_count(account.address),
    })
    signed = w3.eth.account.sign_transaction(tx, account.key)
    w3.eth.send_raw_transaction(signed.raw_transaction)
    ```
  </Tab>
</Tabs>

<Warning>
  Approvals are on-chain transactions that cost gas. You only need to perform them once per venue. Use `venue.exchange` for both CLOB and NegRisk, and additionally `venue.adapter` for NegRisk markets.
</Warning>

## GTC orders (Good-til-cancelled)

GTC orders remain on the orderbook until filled or explicitly cancelled. Specify `price` (in dollars) and `size` (number of shares):

```python theme={null}
market = await market_fetcher.get_market("btc-above-100k-march-2025")

result = await order_client.create_order(
    token_id=market.tokens.yes,
    price=0.65,
    size=10.0,
    side=Side.BUY,
    order_type=OrderType.GTC,
    market_slug="btc-above-100k-march-2025",
)
print("Order placed:", result)
```

### Post-only GTC order

Use `post_only=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.

```python theme={null}
result = await order_client.create_order(
    token_id=market.tokens.yes,
    price=0.65,
    size=10.0,
    side=Side.BUY,
    order_type=OrderType.GTC,
    market_slug="btc-above-100k-march-2025",
    post_only=True,
)
```

| Parameter     | Type        | Description                                                                              |
| ------------- | ----------- | ---------------------------------------------------------------------------------------- |
| `token_id`    | `str`       | Token ID from `market.tokens.yes` or `market.tokens.no`                                  |
| `price`       | `float`     | Price per share in dollars (0.01 to 0.99)                                                |
| `size`        | `float`     | Number of shares to buy or sell                                                          |
| `side`        | `Side`      | `Side.BUY` or `Side.SELL`                                                                |
| `order_type`  | `OrderType` | `OrderType.GTC`                                                                          |
| `market_slug` | `str`       | Market slug for venue lookup                                                             |
| `post_only`   | `bool`      | Optional. When `True`, rejects the order if it would immediately match. Default `False`. |

## Self-trade prevention

Pass `stp_policy` 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`.

```python theme={null}
result = await order_client.create_order(
    token_id=market.tokens.yes,
    price=0.65,
    size=10.0,
    side=Side.BUY,
    order_type=OrderType.GTC,
    market_slug="btc-above-100k-march-2025",
    stp_policy="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:

```python theme={null}
execution = result.execution
if execution.settlement_status == "CANCELED" and execution.reason == "STP_TAKER_REJECTED":
    print("Order rejected by self-trade prevention")
elif execution.stp_maker_cancels:
    print("Cancelled own resting orders:", execution.stp_maker_cancels)
```

| `execution` field   | Type                | Description                                                         |
| ------------------- | ------------------- | ------------------------------------------------------------------- |
| `settlement_status` | `str`               | `CANCELED` when a `cancel_taker` / `cancel_both` order is rejected. |
| `reason`            | `str \| None`       | `STP_TAKER_REJECTED` when the incoming order was rejected.          |
| `stp_maker_cancels` | `list[str] \| None` | 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.

`post_only` is not supported for FAK orders.

```python theme={null}
response = await order_client.create_order(
    token_id=market.tokens.yes,
    price=0.45,
    size=10.0,
    side=Side.BUY,
    order_type=OrderType.FAK,
    market_slug="btc-above-100k-march-2025",
)

if response.maker_matches:
    print(f"FAK order matched immediately with {len(response.maker_matches)} fill(s)")
else:
    print("FAK remainder was cancelled.")
```

| Parameter     | Type        | Description                                             |
| ------------- | ----------- | ------------------------------------------------------- |
| `token_id`    | `str`       | Token ID from `market.tokens.yes` or `market.tokens.no` |
| `price`       | `float`     | Price per share in dollars (0.01 to 0.99)               |
| `size`        | `float`     | Number of shares to buy or sell                         |
| `side`        | `Side`      | `Side.BUY` or `Side.SELL`                               |
| `order_type`  | `OrderType` | `OrderType.FAK`                                         |
| `market_slug` | `str`       | Market slug for venue lookup                            |

## FOK orders (Fill-or-kill)

FOK orders execute immediately and fully, or are rejected entirely. Instead of `price` and `size`, you specify `maker_amount`:

<Tabs>
  <Tab title="FOK BUY">
    When buying, `maker_amount` is the **total USDC you want to spend**. The exchange fills as many shares as possible at the best available price:

    ```python theme={null}
    result = await order_client.create_order(
        token_id=market.tokens.yes,
        maker_amount=10.0,  # spend 10 USDC
        side=Side.BUY,
        order_type=OrderType.FOK,
        market_slug="btc-above-100k-march-2025",
    )
    ```
  </Tab>

  <Tab title="FOK SELL">
    When selling, `maker_amount` is the **number of shares to sell**. The exchange returns USDC at the best available price:

    ```python theme={null}
    result = await order_client.create_order(
        token_id=market.tokens.yes,
        maker_amount=10.0,  # sell 10 shares
        side=Side.SELL,
        order_type=OrderType.FOK,
        market_slug="btc-above-100k-march-2025",
    )
    ```
  </Tab>
</Tabs>

## AMM trading

CLOB orders trade against the orderbook. AMM (FPMM) markets trade against a pool, and the SDK exposes them through `client.partner_amm`. The service calls `POST /amm/allowances/check`, `POST /amm/allowances/approve`, `POST /amm/buy`, and `POST /amm/sell` on behalf of a partner server wallet.

Use it when the market is an AMM market and you want the server to hold custody, sign the trade, and pay gas. See [AMM Trading (Server Wallets)](/developers/amm-trading) for the underlying endpoints and market model.

### Requirements

* Authenticate with an HMAC API token that holds **both** the `trading` and `delegated_signing` scopes, or pass a per-call Privy `identity_token`. Legacy `x-api-key` credentials are rejected.
* The trade runs against a server-wallet sub-account. Set `on_behalf_of` to the sub-account profile ID, or omit it to trade from the authenticated profile.
* Amounts are **positive integer strings** in the collateral token's base units (for USDC: `"1000000"` = 1 USDC). Never pass floats.
* `slippage_bps` is optional and ranges from `0` to `1000`. The server default is `100` (1%).
* `outcome_index` is `0` for YES and `1` for NO.

### One-time approval per wallet and market

`BUY` and `SELL` approvals are independent and set up once per wallet and market. `buy` and `sell` do **not** preflight allowances themselves. Confirm the allowance first.

`ensure_allowance` runs `check_allowance`, submits `approve_allowance` at most once when missing, then polls the check until `confirmed` is true. Tune the poll with `interval` (default `2` seconds) and `max_attempts` (default `30`).

```python theme={null}
from limitless_sdk import Client, HMACCredentials, AmmAllowanceParams

client = Client(
    hmac_credentials=HMACCredentials(
        token_id=os.environ["LMTS_TOKEN_ID"],
        secret=os.environ["LMTS_TOKEN_SECRET"],
    ),
)

market_slug = "btc-100k-weekly"
child_profile_id = 12345

await client.partner_amm.ensure_allowance(
    AmmAllowanceParams(market=market_slug, side="BUY", on_behalf_of=child_profile_id)
)
await client.partner_amm.ensure_allowance(
    AmmAllowanceParams(market=market_slug, side="SELL", on_behalf_of=child_profile_id)
)
```

<Note>
  A `submitted` response from `approve_allowance` (HTTP 202) is not confirmation. Either use `ensure_allowance`, or poll `check_allowance` until `confirmed` is true.
</Note>

### Buy shares

`buy` spends an exact collateral amount on the chosen outcome. Pass a unique `idempotency_key` per trade. On a timeout retry, reuse the exact same params so the server replays the original submission rather than opening a second trade.

```python theme={null}
from limitless_sdk import AmmBuyParams

buy = await client.partner_amm.buy(
    AmmBuyParams(
        market=market_slug,
        outcome_index=0,               # 0 = YES, 1 = NO
        collateral_amount="1000000",   # 1 USDC in base units
        slippage_bps=100,              # optional, 0..1000
        idempotency_key="buy-unique-key-001",
        on_behalf_of=child_profile_id, # omit for a direct profile
    )
)

print(buy.status, buy.expected_shares, buy.min_shares)
```

### Sell shares

`sell` requests an exact collateral return by selling outcome shares.

```python theme={null}
from limitless_sdk import AmmSellParams

sell = await client.partner_amm.sell(
    AmmSellParams(
        market=market_slug,
        outcome_index=0,
        collateral_return_amount="992015",
        idempotency_key="sell-unique-key-001",
        on_behalf_of=child_profile_id,
    )
)

print(sell.status, sell.expected_shares, sell.max_shares)
```

<Note>
  Reusing an `idempotency_key` with different params raises `ConflictError` (HTTP 409). The four AMM routes share a rate limit of 10 requests / 10 seconds per actor. Pass `with_raw_response=True` to any AMM method to receive an `HttpRawResponse` exposing `status`, `headers`, and `data`.
</Note>

## Cancelling orders

<Tabs>
  <Tab title="Cancel a single order">
    Cancel a specific order by its ID:

    ```python theme={null}
    await order_client.cancel(order_id="abc123-def456")
    ```
  </Tab>

  <Tab title="Cancel all orders on a market">
    Cancel every open order you have on a given market:

    ```python theme={null}
    await order_client.cancel_all(market_slug="btc-above-100k-march-2025")
    ```
  </Tab>
</Tabs>

## Cancel and replace

`cancel_replace` cancels one open order and submits a replacement in the same request. Use it to reprice or resize a resting order in one round-trip instead of separate cancel and create calls. `cancel_replace_batch` runs several of these operations in a single call.

The cancel and the replacement are **not atomic**: they report independent outcomes, and a successful cancel does not guarantee a successful replacement. Choose the failure mode with `CancelReplaceMode`:

| Mode              | Cancellation fails      | Replacement result                               |
| ----------------- | ----------------------- | ------------------------------------------------ |
| `STOP_ON_FAILURE` | Stops the operation     | `NOT_ATTEMPTED`                                  |
| `ALLOW_FAILURE`   | Continues the operation | `SUCCESS`, `FAILURE`, or `UNKNOWN` independently |

Identify the order to cancel by exactly one of `order_id` or `client_order_id`. The replacement is a normal signed order and uses the same fields as `create_order` (`order_type`, `market_slug`, `token_id`, `side`, `price`, `size` or `maker_amount`, `post_only`, `stp_policy`, etc.).

### Single cancel-replace

```python theme={null}
from limitless_sdk.types import CancelReplaceMode, OrderType, Side

result = await order_client.cancel_replace(
    order_id="order_abc123",
    mode=CancelReplaceMode.STOP_ON_FAILURE,
    market_slug=market.slug,
    order_type=OrderType.GTC,
    token_id=market.tokens.yes,
    side=Side.BUY,
    price=0.62,
    size=100,
)

if result.cancel.status == "SUCCESS":
    print("Cancelled:", result.cancel.order_id)
if result.replacement.status == "SUCCESS":
    print("Replacement placed:", result.replacement.data.id)
```

### Batch cancel-replace

Each operation is a dict of the same keyword arguments accepted by `cancel_replace`. Results come back with the caller's `index`:

```python theme={null}
batch = await order_client.cancel_replace_batch([
    {
        "order_id": "order_abc123",
        "mode": CancelReplaceMode.ALLOW_FAILURE,
        "market_slug": market.slug,
        "order_type": OrderType.GTC,
        "token_id": market.tokens.yes,
        "side": Side.BUY,
        "price": 0.62,
        "size": 100,
    },
    {
        "client_order_id": "my-tag-42",
        "mode": CancelReplaceMode.STOP_ON_FAILURE,
        "market_slug": market.slug,
        "order_type": OrderType.GTC,
        "token_id": market.tokens.no,
        "side": Side.SELL,
        "price": 0.41,
        "size": 50,
    },
])

for item in batch.results:
    print(item.index, item.cancel.status, item.replacement.status)
```

### Delegated cancel-replace

Partners with the `delegated_signing` scope call `delegated_order_service.cancel_replace` and `.cancel_replace_batch`. The server signs the replacement using the sub-account's managed wallet, so no private key is required. Pass `on_behalf_of` (the sub-account profile ID) on every operation:

```python theme={null}
await delegated_order_service.cancel_replace(
    order_id="order_abc123",
    mode=CancelReplaceMode.STOP_ON_FAILURE,
    on_behalf_of=partner_account.profile_id,
    market_slug=market.slug,
    order_type=OrderType.GTC,
    token_id=market.tokens.yes,
    side=Side.BUY,
    price=0.62,
    size=100,
)
```

<Note>
  See [`POST /orders/cancel-replace`](/api-reference/trading/cancel-replace) and [`POST /orders/cancel-replace/batch`](/api-reference/trading/cancel-replace-batch) for the full request and response shapes, per-status fields, and failure semantics.
</Note>

## Enums reference

### Side

| Value       | Description          |
| ----------- | -------------------- |
| `Side.BUY`  | Buy shares with USDC |
| `Side.SELL` | Sell shares for USDC |

### OrderType

| Value           | Description                                                 |
| --------------- | ----------------------------------------------------------- |
| `OrderType.GTC` | Good-til-cancelled limit order (rests on the book)          |
| `OrderType.FAK` | Fill-and-kill limit order (cancels any unmatched remainder) |
| `OrderType.FOK` | Fill-or-kill market order (fills immediately or rejects)    |

## Error handling

The SDK raises `APIError` for non-2xx responses. Always wrap order calls in try/except:

```python theme={null}
from limitless_sdk.api import APIError

try:
    result = await order_client.create_order(
        token_id=market.tokens.yes,
        price=0.65,
        size=10.0,
        side=Side.BUY,
        order_type=OrderType.GTC,
        market_slug="btc-above-100k-march-2025",
    )
except APIError as e:
    print(f"Order failed — status {e.status_code}: {e.message}")
```

<Note>
  See [Error Handling & Retry](/developers/sdk/python/error-handling) for details on `APIError` fields and the `@retry_on_errors` decorator.
</Note>

## Complete example

```python theme={null}
import asyncio
import os
from eth_account import Account
from limitless_sdk.api import HttpClient, APIError
from limitless_sdk import HMACCredentials
from limitless_sdk.markets import MarketFetcher
from limitless_sdk.orders import OrderClient
from limitless_sdk.types import Side, OrderType

async def main():
    http_client = HttpClient(
        hmac_credentials=HMACCredentials(
            token_id=os.environ["LMTS_TOKEN_ID"],
            secret=os.environ["LMTS_TOKEN_SECRET"],
        ),
    )
    account = Account.from_key("0xYOUR_PRIVATE_KEY")
    market_fetcher = MarketFetcher(http_client)
    order_client = OrderClient(http_client, account)

    try:
        # Fetch market (caches venue automatically)
        market = await market_fetcher.get_market("btc-above-100k-march-2025")

        # Place a GTC BUY order for 10 YES shares at $0.65
        result = await order_client.create_order(
            token_id=market.tokens.yes,
            price=0.65,
            size=10.0,
            side=Side.BUY,
            order_type=OrderType.GTC,
            market_slug="btc-above-100k-march-2025",
        )
        print("GTC order placed:", result)

        # Place a FAK BUY order for 10 YES shares at $0.45
        fak_result = await order_client.create_order(
            token_id=market.tokens.yes,
            price=0.45,
            size=10.0,
            side=Side.BUY,
            order_type=OrderType.FAK,
            market_slug="btc-above-100k-march-2025",
        )
        print("FAK order placed:", fak_result)

        # Place a FOK BUY order spending 5 USDC
        fok_result = await order_client.create_order(
            token_id=market.tokens.yes,
            maker_amount=5.0,
            side=Side.BUY,
            order_type=OrderType.FOK,
            market_slug="btc-above-100k-march-2025",
        )
        print("FOK order placed:", fok_result)

        # Cancel all orders on this market
        await order_client.cancel_all(market_slug="btc-above-100k-march-2025")
        print("All orders cancelled")

    except APIError as e:
        print(f"API error — status {e.status_code}: {e.message}")
    finally:
        await http_client.close()

asyncio.run(main())
```

<Warning>
  Always call `await http_client.close()` when finished. Failing to close the client can leave open connections and cause resource leaks.
</Warning>
