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

## Overview

The `OrderClient` handles order creation, EIP-712 signing, and order management. It supports Good-Till-Cancelled (GTC), Fill-And-Kill (FAK), and Fill-or-Kill (FOK) orders.

## Prerequisites

Before placing orders, you need three components:

```go theme={null}
import (
    "context"
    "log"

    limitless "github.com/limitless-labs-group/limitless-exchange-go-sdk/limitless"
)

client := limitless.NewHttpClient()   // loads LIMITLESS_API_KEY from env
marketFetcher := limitless.NewMarketFetcher(client)

orderClient, err := limitless.NewOrderClient(
    client,
    "0xYOUR_PRIVATE_KEY",  // hex-encoded private key (with or without "0x" prefix)
)
if err != nil {
    log.Fatal(err)
}
```

| Component       | Purpose                                        |
| --------------- | ---------------------------------------------- |
| `HttpClient`    | Authenticated HTTP client for API requests     |
| `MarketFetcher` | Fetches market data and caches venue addresses |
| `OrderClient`   | Creates, signs, and submits orders             |

<Note>
  The `OrderClient` lazily fetches your user profile on the first order to determine your fee rate. The `CHAIN_ID` environment variable defaults to `8453` (Base mainnet).
</Note>

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

    ```go theme={null}
    market, _ := marketFetcher.GetMarket(ctx, "your-market-slug")
    exchange := market.Venue.Exchange

    // Use go-ethereum or any Ethereum client to send approval transactions:
    // 1. Approve USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) to the exchange for BUY orders
    // 2. Approve Conditional Tokens (0xC9c98965297Bc527861c898329Ee280632B76e18) to the exchange for SELL orders
    ```
  </Tab>

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

    ```go theme={null}
    market, _ := marketFetcher.GetMarket(ctx, "your-negrisk-slug")
    exchange := market.Venue.Exchange
    adapter := *market.Venue.Adapter

    // 1. Approve USDC to the exchange (same as CLOB)
    // 2. Approve Conditional Tokens to the exchange (same as CLOB)
    // 3. Approve Conditional Tokens to the adapter (NegRisk only)
    ```
  </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-Till-Cancelled)

GTC orders remain on the orderbook until filled or explicitly cancelled. Specify `Price` (0.0–1.0, tick-aligned to 0.001) and `Size` (number of shares):

```go theme={null}
ctx := context.Background()
market, err := marketFetcher.GetMarket(ctx, "btc-above-100k-march-2025")
if err != nil {
    log.Fatal(err)
}

result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
    OrderType:  limitless.OrderTypeGTC,
    MarketSlug: "btc-above-100k-march-2025",
    Args: limitless.GTCOrderArgs{
        TokenID: market.Tokens.Yes,
        Side:    limitless.SideBuy,
        Price:   0.65,
        Size:    10.0,
    },
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Order placed: %+v\n", result.Order)
```

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

```go theme={null}
result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
    OrderType:  limitless.OrderTypeGTC,
    MarketSlug: "btc-above-100k-march-2025",
    Args: limitless.GTCOrderArgs{
        TokenID:  market.Tokens.Yes,
        Side:     limitless.SideBuy,
        Price:    0.65,
        Size:     10.0,
        PostOnly: true,
    },
})
```

### GTCOrderArgs

| Field        | Type      | Description                                                                                                 |
| ------------ | --------- | ----------------------------------------------------------------------------------------------------------- |
| `TokenID`    | `string`  | Token ID from `Market.Tokens.Yes` or `Market.Tokens.No`                                                     |
| `Side`       | `Side`    | `SideBuy` (0) or `SideSell` (1)                                                                             |
| `Price`      | `float64` | Price per share (0.0–1.0, must be tick-aligned to 0.001)                                                    |
| `Size`       | `float64` | Number of shares to buy or sell                                                                             |
| `PostOnly`   | `bool`    | Optional. When `true`, rejects the order if it would immediately match. Default `false`.                    |
| `Expiration` | `string`  | Must be `"0"` (the default). Non-zero expiration is not currently supported and is rejected by the API.     |
| `Nonce`      | `*int`    | Must be `0` (the default when `nil`). Non-zero nonce is not currently supported and is rejected by the API. |
| `Taker`      | `string`  | Optional taker address (defaults to zero address)                                                           |

## Self-Trade Prevention

Set `StpPolicy` on `CreateOrderParams` 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 args — and applies to any order type. Leave it empty to keep the server default, `cancel_maker`.

```go theme={null}
result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
    OrderType:  limitless.OrderTypeGTC,
    MarketSlug: "btc-above-100k-march-2025",
    StpPolicy:  "cancel_maker", // "cancel_maker" | "cancel_taker" | "cancel_both"
    Args: limitless.GTCOrderArgs{
        TokenID: market.Tokens.Yes,
        Side:    limitless.SideBuy,
        Price:   0.65,
        Size:    10.0,
    },
})
```

| 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` field with the outcome:

```go theme={null}
if result.Execution.SettlementStatus == "CANCELED" && result.Execution.Reason == "STP_TAKER_REJECTED" {
    fmt.Println("Order rejected by self-trade prevention")
} else if len(result.Execution.StpMakerCancels) > 0 {
    fmt.Printf("Cancelled own resting orders: %v\n", 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.

```go theme={null}
result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
    OrderType:  limitless.OrderTypeFAK,
    MarketSlug: "btc-above-100k-march-2025",
    Args: limitless.FAKOrderArgs{
        TokenID: market.Tokens.Yes,
        Side:    limitless.SideBuy,
        Price:   0.45,
        Size:    10.0,
    },
})
if err != nil {
    log.Fatal(err)
}

if len(result.MakerMatches) > 0 {
    fmt.Printf("FAK order matched immediately with %d fill(s)\n", len(result.MakerMatches))
} else {
    fmt.Println("FAK remainder was cancelled.")
}
```

### FAKOrderArgs

| Field        | Type      | Description                                                                                                 |
| ------------ | --------- | ----------------------------------------------------------------------------------------------------------- |
| `TokenID`    | `string`  | Token ID from `Market.Tokens.Yes` or `Market.Tokens.No`                                                     |
| `Side`       | `Side`    | `SideBuy` (0) or `SideSell` (1)                                                                             |
| `Price`      | `float64` | Price per share (0.0–1.0, must be tick-aligned to 0.001)                                                    |
| `Size`       | `float64` | Number of shares to buy or sell                                                                             |
| `Expiration` | `string`  | Must be `"0"` (the default). Non-zero expiration is not currently supported and is rejected by the API.     |
| `Nonce`      | `*int`    | Must be `0` (the default when `nil`). Non-zero nonce is not currently supported and is rejected by the API. |
| `Taker`      | `string`  | Optional taker address (defaults to zero address)                                                           |

## FOK Orders (Fill-or-Kill)

FOK orders execute immediately and fully, or are rejected entirely. Instead of `Price` and `Size`, you specify `MakerAmount`:

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

    ```go theme={null}
    result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
        OrderType:  limitless.OrderTypeFOK,
        MarketSlug: "btc-above-100k-march-2025",
        Args: limitless.FOKOrderArgs{
            TokenID:     market.Tokens.Yes,
            Side:        limitless.SideBuy,
            MakerAmount: 10.0, // spend 10 USDC
        },
    })
    ```
  </Tab>

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

    ```go theme={null}
    result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
        OrderType:  limitless.OrderTypeFOK,
        MarketSlug: "btc-above-100k-march-2025",
        Args: limitless.FOKOrderArgs{
            TokenID:     market.Tokens.Yes,
            Side:        limitless.SideSell,
            MakerAmount: 10.0, // sell 10 shares
        },
    })
    ```
  </Tab>
</Tabs>

### FOKOrderArgs

| Field         | Type      | Description                                                                                                 |
| ------------- | --------- | ----------------------------------------------------------------------------------------------------------- |
| `TokenID`     | `string`  | Token ID from `Market.Tokens.Yes` or `Market.Tokens.No`                                                     |
| `Side`        | `Side`    | `SideBuy` (0) or `SideSell` (1)                                                                             |
| `MakerAmount` | `float64` | USDC to spend (buy) or shares to sell (sell), max 6 decimal places                                          |
| `Expiration`  | `string`  | Must be `"0"` (the default). Non-zero expiration is not currently supported and is rejected by the API.     |
| `Nonce`       | `*int`    | Must be `0` (the default when `nil`). Non-zero nonce is not currently supported and is rejected by the API. |
| `Taker`       | `string`  | Optional taker address (defaults to zero address)                                                           |

## Advanced: Build and Sign Separately

For advanced use cases, you can build and sign orders without submitting them:

```go theme={null}
// Build an unsigned order
unsigned, err := orderClient.BuildUnsignedOrder(ctx, limitless.GTCOrderArgs{
    TokenID: market.Tokens.Yes,
    Side:    limitless.SideBuy,
    Price:   0.65,
    Size:    10.0,
})
if err != nil {
    log.Fatal(err)
}

// Sign the order
signature, err := orderClient.SignOrder(unsigned)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Signature:", signature)
```

## Cancelling Orders

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

    ```go theme={null}
    msg, err := orderClient.Cancel(ctx, "abc123-def456")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(msg)
    ```
  </Tab>

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

    ```go theme={null}
    msg, err := orderClient.CancelAll(ctx, "btc-above-100k-march-2025")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(msg)
    ```
  </Tab>
</Tabs>

## Enums Reference

### Side

| Constant   | Value | Description          |
| ---------- | ----- | -------------------- |
| `SideBuy`  | `0`   | Buy shares with USDC |
| `SideSell` | `1`   | Sell shares for USDC |

### OrderType

| Constant       | Value   | Description                                                 |
| -------------- | ------- | ----------------------------------------------------------- |
| `OrderTypeGTC` | `"GTC"` | Good-Till-Cancelled limit order (rests on the book)         |
| `OrderTypeFAK` | `"FAK"` | Fill-And-Kill limit order (cancels any unmatched remainder) |
| `OrderTypeFOK` | `"FOK"` | Fill-or-Kill market order (fills immediately or rejects)    |

## Error Handling

The SDK returns typed errors for order failures. Use `errors.As()` to inspect them:

```go theme={null}
import "errors"

result, err := orderClient.CreateOrder(ctx, params)
if err != nil {
    var apiErr *limitless.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("Order failed — status %d: %s\n", apiErr.Status, apiErr.Message)
    }

    var validErr *limitless.OrderValidationError
    if errors.As(err, &validErr) {
        fmt.Printf("Validation error on field %s: %s\n", validErr.Field, validErr.Message)
    }
}
```

<Note>
  See [Error Handling & Retry](/developers/sdk/go/error-handling) for details on error types and the `WithRetry` function.
</Note>

## Complete Example

```go theme={null}
package main

import (
    "context"
    "errors"
    "fmt"
    "log"

    limitless "github.com/limitless-labs-group/limitless-exchange-go-sdk/limitless"
)

func main() {
    client := limitless.NewHttpClient()
    marketFetcher := limitless.NewMarketFetcher(client)
    ctx := context.Background()

    orderClient, err := limitless.NewOrderClient(client, "0xYOUR_PRIVATE_KEY")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Wallet:", orderClient.WalletAddress())

    // Fetch market (caches venue automatically)
    market, err := marketFetcher.GetMarket(ctx, "btc-above-100k-march-2025")
    if err != nil {
        log.Fatal(err)
    }

    // Place a GTC BUY order for 10 YES shares at $0.65
    result, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
        OrderType:  limitless.OrderTypeGTC,
        MarketSlug: "btc-above-100k-march-2025",
        Args: limitless.GTCOrderArgs{
            TokenID: market.Tokens.Yes,
            Side:    limitless.SideBuy,
            Price:   0.65,
            Size:    10.0,
        },
    })
    if err != nil {
        var apiErr *limitless.APIError
        if errors.As(err, &apiErr) {
            log.Fatalf("API error — status %d: %s", apiErr.Status, apiErr.Message)
        }
        log.Fatal(err)
    }
    fmt.Printf("GTC order placed: %+v\n", result.Order)

    // Place a FAK BUY order for 10 YES shares at $0.45
    fakResult, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
        OrderType:  limitless.OrderTypeFAK,
        MarketSlug: "btc-above-100k-march-2025",
        Args: limitless.FAKOrderArgs{
            TokenID: market.Tokens.Yes,
            Side:    limitless.SideBuy,
            Price:   0.45,
            Size:    10.0,
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("FAK order placed: %+v\n", fakResult.Order)

    // Place a FOK BUY order spending 5 USDC
    fokResult, err := orderClient.CreateOrder(ctx, limitless.CreateOrderParams{
        OrderType:  limitless.OrderTypeFOK,
        MarketSlug: "btc-above-100k-march-2025",
        Args: limitless.FOKOrderArgs{
            TokenID:     market.Tokens.Yes,
            Side:        limitless.SideBuy,
            MakerAmount: 5.0,
        },
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("FOK order placed: %+v\n", fokResult.Order)

    // Cancel all orders on this market
    msg, err := orderClient.CancelAll(ctx, "btc-above-100k-march-2025")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Cancelled:", msg)
}
```
