Skip to content
All library documents

Polymarket Trading Integration: Wallets, Orders, and Data Limits

Article NautilusTrader

Summary

This technical guide explains how NautilusTrader connects to Polymarket’s central limit order book for binary outcome tokens. It outlines market data and execution components, wallet signature types, pUSD collateral, allowance setup, session keys, and Deposit Wallet position operations. Its operational guidance distinguishes direct EOA approvals from smart wallet approval flows and describes refreshing and checking allowance data before relying on it.

The guide also documents practical data and lifecycle limits: the condition feed has an approximate three-year retention horizon, pagination can cap returned history, and end-only trade queries may require traversing many newer pages. Cached instruments are retired after the venue confirms closure, while scheduled end dates alone do not establish that trading has stopped. These details help inform integration and backtest design, but the document is adapter documentation rather than a trading strategy or evidence of market edge; feed retention and venue behavior constrain what can be observed.

Key ideas

  • Polymarket outcome tokens are represented as binary option instruments, with pUSD used as collateral.
  • Wallet signature type determines the account setup and approval process.
  • Allowance evidence should be decoded strictly before it is used to establish approval status.
  • Trade history is constrained by pagination and the venue’s approximate three-year retention window.
  • A scheduled market end date does not by itself confirm that trading has stopped.

Tags

Full text
# Polymarket


# Polymarket

Founded in 2020, Polymarket is a decentralized prediction market platform that enables
traders to speculate on event outcomes by buying and selling outcome tokens.

NautilusTrader provides a venue integration for data and execution via Polymarket's Central Limit
Order Book (CLOB) API.

The adapter is implemented in Rust and exposed to Python at
`nautilus_trader.adapters.polymarket`; data, execution, signing, and WebSocket
operations therefore have the same behavior from Rust and Python.

The adapter handles order preparation and signing for several wallet configurations. This guide
covers market data, trade execution, session key administration, and Deposit Wallet position operations.

## Installation

The Python package includes the Polymarket adapter; no adapter-specific extra is required.

To install the latest pre-release build:

```bash
uv pip install --pre nautilus_trader
```

To build the Python package from source, run from the repository root:

```bash
make build-debug
```

For development wheels and source-build prerequisites, see the
[installation guide](../getting_started/installation.md).

## Examples

The maintained examples are available in
[`crates/adapters/polymarket/examples`](https://github.com/nautechsystems/nautilus_trader/tree/develop/crates/adapters/polymarket/examples)
for Rust. For Python, use the Rust-native [data tester](https://github.com/nautechsystems/nautilus_trader/blob/develop/examples/live/polymarket/data_tester.py),
[execution tester](https://github.com/nautechsystems/nautilus_trader/blob/develop/examples/live/polymarket/exec_tester.py),
or [Up/Down smoke tester](https://github.com/nautechsystems/nautilus_trader/blob/develop/examples/live/polymarket/updown_smoke_tester.py).
The exec tester configurations apply the
[close precision](#exec-tester-close-residuals) needed for Polymarket market SELL orders.

## Binary options

A [binary option](https://en.wikipedia.org/wiki/Binary_option) is a type of financial exotic
option contract in which traders bet on the outcome of a yes-or-no proposition. If the
prediction is correct, the trader receives a fixed payout; otherwise, they receive nothing.
NautilusTrader represents Polymarket outcome tokens as `BinaryOption` instruments.

Polymarket uses [pUSD](#pusd) as the collateral token for trading.

## Polymarket documentation

Polymarket offers resources for different audiences:

- [Polymarket Learn](https://learn.polymarket.com/): Educational content and guides for users
  to understand the platform and how to engage with it.
- [Polymarket CLOB API](https://docs.polymarket.com/getting-started/api): Technical
  documentation for developers interacting with the Polymarket CLOB API.

## Overview

This guide assumes a trader is setting up for both live market data feeds and trade execution.
The Rust implementation includes multiple components, which can be used together or separately
depending on the use case.

- `PolymarketWebSocketClient`: Low-level WebSocket API connectivity built on the Nautilus Rust
  `WebSocketClient`.
- `PolymarketInstrumentProvider`: Instrument parsing and loading functionality for `BinaryOption`
  instruments.
- `PolymarketDataClient`: A market data feed manager.
- `PolymarketExecutionClient`: A trade execution gateway.
- `PolymarketDataClientFactory`: Factory for Polymarket data clients (used by the live node
  builder).
- `PolymarketExecutionClientFactory`: Factory for Polymarket execution clients (used by the live
  node builder).
- `PolymarketPositionClient`: Deposit Wallet split, merge, and redeem operations.
- `PolymarketSessionKeyClient`: Owner-operated session authorization, listing, and revocation.

:::note
Python users configure live nodes through the exported configuration and factory classes, and call
position operations through `PolymarketPositionClient` and session administration through
`PolymarketSessionKeyClient`. The direct WebSocket, provider, data client, and execution client types
are Rust-only implementation components.
:::

## pUSD

**pUSD** is the collateral token used for trading on Polymarket. It is a standard ERC-20 token on
Polygon, backed by USDC.

The proxy contract address is
[0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB](https://polygonscan.com/address/0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB)
on Polygon. Direct on-chain funding wraps Polygon USDC.e (bridged USDC) into pUSD
through the [CollateralOnramp](https://docs.polymarket.com/resources/contracts).
The Bridge API can also deposit supported assets from other chains and credit pUSD
after conversion.

## Wallets and accounts

To trade on Polymarket via NautilusTrader, use a Polygon-compatible wallet, such as MetaMask.

### Signature types

Polymarket supports multiple signature types for order signing and verification:

| Signature Type | Wallet Type                    | Description                                                              | Use Case                                                                                       |
| -------------- | ------------------------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| `0`            | EOA (Externally Owned Account) | Standard EIP712 signatures from wallets with direct private key control. | **Adapter default.** Allowlisted EOA trading where the funder and signer are the same address. |
| `1`            | Proxy Wallet                   | Legacy smart contract wallet created through email or social login.      | Requires the Proxy Wallet `funder` address.                                                    |
| `2`            | Safe Wallet                    | Legacy Gnosis Safe wallet created with an external browser wallet.       | Requires the Safe Wallet `funder` address.                                                     |
| `3`            | Deposit Wallet                 | ERC-1271 smart wallet used for new Polymarket account wallets.           | Requires the Deposit Wallet `funder`; API credentials stay bound to the signer.                |

:::info
Polymarket uses Deposit Wallets for account wallets deployed on or after May 4, 2026. Direct EOA
trading requires an allowlisted EOA. See the Polymarket
[wallet and authentication guide](https://docs.polymarket.com/trading/wallets-auth) for the account
types and setup flows.
:::

NautilusTrader defaults to signature type 0 (EOA). Set `signature_type` to use another supported
wallet type. Proxy signature clients fail during construction unless `funder` is present and differs
from the signing address.

A single wallet address is supported per trader instance when using environment variables, or
multiple wallets can be configured through multiple execution client instances.

Fund your wallet with pUSD before submitting orders. An unfunded wallet produces the
"not enough balance or allowance" API error.

### Setting EOA allowances

The adapter includes a direct on-chain allowance command for EOA accounts. Use it only when the
funding wallet is the signer (`PolymarketSignatureType::Eoa`). Fund the EOA with POL for gas, set
`POLYMARKET_PK`, and run:

```bash
cargo run -p nautilus-polymarket --bin polymarket-set-allowances
```

The command grants maximum pUSD and CTF approvals to the CTF Exchange, Neg Risk CTF Exchange, and
`NegRiskCtfCollateralAdapter`. It uses `https://polygon.drpc.org` by default; set
`POLYGON_RPC_URL` to use another Polygon RPC endpoint. Run it again if Polymarket changes the
required contracts.

The command grants approvals only; it does not revoke approvals for contracts that are no longer
targets. Treat revocation as a separate on-chain operation and confirm that no remaining redemption
or settlement flow depends on the legacy approval before submitting it.

### Setting smart-wallet allowances

Do not run the EOA command for a proxy, Safe, or Deposit Wallet funder. It signs transactions from
the EOA key and cannot grant approvals from a smart contract wallet.

Use Polymarket's [wallet and authentication flow](https://docs.polymarket.com/trading/wallets-auth)
to submit the approvals from the account wallet. Deposit Wallet approvals use an ordered `WALLET`
batch authorized by the signer and submitted through the Relayer. Safe and Proxy Wallet approvals
need their wallet-specific SDK payloads.

### Refreshing and verifying allowances

After the approval transaction confirms, refresh the CLOB cache. Rust callers can use
`PolymarketClobHttpClient::update_balance_allowance` with `AssetType::Collateral` for pUSD. Use
`AssetType::Conditional` with a conditional token ID for a conditional-token allowance. Both forms
also need the account's signature type. The authenticated request maps to
`GET /balance-allowance/update`. Use `PolymarketSignatureType::Poly1271` for a Deposit Wallet.

The balance-allowance endpoint has two decoding paths:

| Path                                              | Used for                                             | Allowance handling                                                                                                                                                             | Meaning of success                                                                 |
| ------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| `PolymarketClobHttpClient::get_balance_allowance` | Reading spender allowance evidence.                  | Requires the plural `allowances` map; rejects a missing map, a non-null legacy singular value, malformed or non-canonical keys, and semantic duplicates such as case variants. | Balance plus an unambiguous map; required targets and amounts still need checking. |
| Internal balance-only projection                  | Account state refresh and market-buy fee adjustment. | Ignores allowance fields; its return type cannot expose or grant approval authority.                                                                                           | Balance only; required CLOB spender approvals remain unproven.                     |

Use the strict path whenever a decision depends on allowance evidence so ambiguous wire data cannot
become approval authority.

### Account balances

#### Balance calculation

Balance refreshes use the venue-reported pUSD total and derive locked collateral from the
adapter's local cache of open BUY order reservations for the execution client's account, plus own
BUY fill notional and commission that the venue total may still include. Each reservation uses the
cached limit price and remaining quantity. Submitted orders awaiting acceptance and orders absent
from the execution cache are not included. SELL orders reserve outcome tokens rather than pUSD and
do not contribute to locked collateral.

The next refresh after a BUY fill is known subtracts that fill's notional plus commission from
free, together with any other unsettled BUY spend still measured against the same snapshot. The
cohort releases together when the venue total has fallen, since that snapshot, by at least the
combined amount, so a drained total is not subtracted again. Until then, locked unsettled spend is
only the part of the open spend that the decrease has not covered. A refresh whose venue total is
unchanged keeps the open spend locked. A void removes that fill's spend from locked collateral
immediately. Later void revisions are cumulative, so only the increase is removed. The voided
amount still counts toward the decrease required to release other open spend. A void of a fill that
was never locked does not raise that threshold. When no open spend remains, the next refresh
releases the voided amount and moves the snapshot.
A BUY fill first applied from terminal REST evidence, including a confirmed fill report, is not
locked, because that evidence is admitted only after the trade is confirmed.
A reconciled BUY fill more than 15 seconds older than that snapshot, or received before any
snapshot, is not locked. Locked collateral is capped at the reported total, with free balance equal
to total minus locked, following the [balance model](../concepts/accounting.md#balance-model).

#### Refresh timing

The adapter seeds reservations from the execution cache at connect and updates reservations and
unsettled BUY spend as the core processes order events, including reconciled events. A transport
reconnect keeps unsettled BUY spend. Reset clears it. The initial balance refresh precedes startup
reconciliation; orders discovered during reconciliation affect balances on the next refresh.
Refreshes occur at connect, on account queries, after finalized trade updates, and on user
WebSocket reconnect. A balance response that arrives after a newer refresh, after an
execution-client reconnect, or after the client resets, is not applied. A user WebSocket reconnect
does not discard an in-flight refresh; a newer refresh still wins. Account balances do not change
on each order event.
Balance refreshes do not request open orders.

:::warning
These balances are estimates: HTTP balance responses and order events can reflect different
points in time. They do not guarantee funds are reserved before submission. A collateral credit,
including own SELL settlement, can leave unsettled BUY spend locked after the venue total already
reflects the drain. That hold is limited to the amount the decrease has not covered. An unrelated
debit can release that spend before the fill has drained. A void whose amount never leaves the
venue total can keep that uncovered shortfall locked while other unsettled spend is still open. A
confirmation still inside the drain window can report terminal REST BUY spend
as free until a later refresh. A refresh that applies before a queued fill is processed can leave
that fill locked after the total has already drained. The adapter does not lock matched spend from
another client on the same funder, or from a fill it has not yet received.
:::

## API keys

### CLOB credentials

The execution client requires CLOB L2 credentials. Create or derive them with Polymarket's
[API authentication flow](https://docs.polymarket.com/getting-started/api#authentication). The
adapter provides a command that reads `POLYMARKET_PK` and prints the created or derived credentials:

```bash
cargo run -p nautilus-polymarket --bin polymarket-create-api-key
```

Set the returned values as:

- `POLYMARKET_API_KEY`
- `POLYMARKET_API_SECRET`
- `POLYMARKET_PASSPHRASE`

The credentials authenticate the private-key signer, not a proxy or Deposit Wallet funder. The
public data client does not require these credentials.

### Relayer credentials

Deposit Wallet split, merge, and redeem operations also require a Relayer API key. Create one under
Settings > API Keys > Relayer API Keys, as described in
[Connect your account](https://docs.polymarket.com/trading/wallets-auth#connect-your-account), then set:

- `POLYMARKET_RELAYER_API_KEY`
- `POLYMARKET_RELAYER_SIGNER_ADDRESS`

`POLYMARKET_RELAYER_SIGNER_ADDRESS` is the signer address shown when the Relayer key is created.
The position client also reads `POLYMARKET_PK` and `POLYMARKET_FUNDER`.

### Builder credentials

Session authorization and revocation require Builder credentials approved for the session-key API.
These are separate from the Relayer API key used for position operations. Follow Polymarket's
[session-key requirements](https://docs.polymarket.com/trading/session-keys), and keep these values
in the owner's administration environment:

- `POLYMARKET_BUILDER_API_KEY`
- `POLYMARKET_BUILDER_API_SECRET`
- `POLYMARKET_BUILDER_PASSPHRASE`

### Deposit Wallet verification

Construction fails when the funder equals the signing address. Before signing, the position client:

- Checks the current and legacy Deposit Wallet addresses predicted by the canonical Polygon factory.
- Requires deployed wallet code and verifies that the signer owns the wallet.
- Reads the signed nonce from the wallet's on-chain counter.

Polygon RPC defaults to `https://polygon.drpc.org`. Pass `base_url_rpc` to the
`PolymarketPositionClient` constructor to use another trusted Polygon endpoint.

## Session keys

Session keys let a separate signer trade for a Deposit Wallet without giving the trading runtime
its owner's private key. The adapter supports CLOB-scoped authorization. See Polymarket's
[session-key documentation](https://docs.polymarket.com/trading/session-keys) for venue eligibility
and Builder approval requirements.

### Administer access

Run `PolymarketSessionKeyClient` in the **owner's administration environment**, using a single
administration process per wallet. Before authorizing a session:

- Supply the owner private key, owner CLOB credentials, Builder credentials, and Deposit Wallet address
  explicitly. The client does not read environment variables itself.
- Generate and securely store the session keypair separately. Pass only its public address to the
  authorization method.

The Python example reads credentials from the administration process's environment and passes them
into the client:

```python
import os

from nautilus_trader.adapters.polymarket import PolymarketSessionKeyClient
from nautilus_trader.adapters.polymarket import PolymarketSessionKeyClientConfig

admin = PolymarketSessionKeyClient(
    PolymarketSessionKeyClientConfig(
        private_key=os.environ["POLYMARKET_PK"],
        api_key=os.environ["POLYMARKET_API_KEY"],
        api_secret=os.environ["POLYMARKET_API_SECRET"],
        passphrase=os.environ["POLYMARKET_PASSPHRASE"],
        builder_api_key=os.environ["POLYMARKET_BUILDER_API_KEY"],
        builder_api_secret=os.environ["POLYMARKET_BUILDER_API_SECRET"],
        builder_passphrase=os.environ["POLYMARKET_BUILDER_PASSPHRASE"],
        funder=os.environ["POLYMARKET_FUNDER"],
    ),
)

# Use the public address derived from the separately stored session private key.
session_address = os.environ["SESSION_ADDRESS"]

# Run these calls from an async function in the administration process.
key = await admin.authorize_session_key(session_address)
keys = await admin.list_session_keys()
```

Optional configuration:

- `base_url_http` overrides the production CLOB endpoint.
- `base_url_relayer` overrides the production Relayer endpoint.
- `proxy_url` configures an HTTP or HTTPS proxy for both clients.

The Rust example provides the same authorization and listing operations:

```bash
cargo run -p nautilus-polymarket --example polymarket-session-keys -- authorize "$SESSION_ADDRESS"
cargo run -p nautilus-polymarket --example polymarket-session-keys -- list
```

Each administration method has a specific completion condition:

| Method                  | Successful return requires                                                      |
| ----------------------- | ------------------------------------------------------------------------------- |
| `authorize_session_key` | On-chain confirmation; registry matches the address, CLOB scope, and expiration |
| `list_session_keys`     | Validated active, unexpired registry entries                                    |
| `revoke_session_key`    | On-chain confirmation; key absent from the active registry                      |

#### Expiration

Authorizations expire after 4,315 hours, matching the official SDK's wire value. Polymarket describes
this period as 180 days, but its raw example and SDK use a value five hours shorter. The returned
`valid_until` is the exact expiration in Unix seconds.

#### Recover an interrupted operation

After an authorization or revocation times out, encounters a transport failure, or is cancelled,
retry with the **same operation, same address, and same client**. The client retains the signed
request and idempotency key and rejects a different mutation until the pending operation resolves.

Keep the client alive until the operation resolves: pending requests are held in memory.
Unresolved-outcome errors include the idempotency key and any known transaction ID for diagnosis.

:::warning
Signed batches have a **600-second deadline**. If an unaccepted request expires, retrying cannot
create a fresh batch, and the client remains blocked. Reconcile the registry and Relayer outcome
before creating a new client. The same reconciliation is required after a process restart.
:::

### Configure session trading

Create or derive CLOB credentials with the **session private key**, using the existing
[CLOB credential workflow](#clob-credentials). Session mode requires:

- Explicit session credentials and `funder`; it does not fall back to owner credentials from the environment.
- `PolymarketSignatureType.Poly1271`; other wallet signature types are rejected.

**Keep owner and Builder credentials outside the trading runtime.**

In this example, `session_private_key` is the separately stored session key, and
`session_api_key`, `session_api_secret`, and `session_passphrase` are its returned CLOB credentials:

```python
from nautilus_trader.adapters.polymarket import PolymarketExecutionClientConfig
from nautilus_trader.adapters.polymarket import PolymarketSignatureType
from nautilus_trader.adapters.polymarket import PolymarketSignerType

execution = PolymarketExecutionClientConfig(
    signer_type=PolymarketSignerType.Session,
    signature_type=PolymarketSignatureType.Poly1271,
    private_key=session_private_key,
    api_key=session_api_key,
    api_secret=session_api_secret,
    passphrase=session_passphrase,
    funder=deposit_wallet_address,
)
```

Existing configurations default to `PolymarketSignerType.Owner`.

#### Reconciliation and restarts

Orders, trades, and notifications are scoped to the session's activity. Reconciliation requires the
session API key to match order ownership; a shared wallet address alone does not establish ownership.

Session cancel-all cancels known order IDs rather than using wallet-wide market cancellation.
Mass status contains session orders and fills but omits wallet-wide positions. Direct position queries
return an error because wallet holdings cannot establish a session's position.

Configure the node accordingly:

- **Position checks**: Set `LiveExecutionEngineConfig.position_check_interval_secs=None` to disable
  periodic position checks for the node.
- **History across restarts**: Configure [cache database persistence](../concepts/cache.md#database-configuration)
  and keep `load_cache=True` to retain order and fill history.

### Revoke or rotate access

Rotate keys from the administration environment. Coordinate outstanding orders and reconciliation
before switching credentials: **a new session does not inherit visibility into an old session's orders**.

To revoke a key, use the administration client created above, from an async function:

```python
await admin.revoke_session_key(session_address)
```

Or run the Rust example:

```bash
cargo run -p nautilus-polymarket --example polymarket-session-keys -- revoke "$SESSION_ADDRESS"
```

:::warning
Expired or revoked permissions cause venue rejections. A live transport connection does not prove
that authorization remains active.
:::

## Position operations

`PolymarketPositionClient` splits pUSD into complete outcome-token sets, merges complete sets back
to pUSD, and redeems resolved positions. The client supports Deposit Wallet (`PolymarketSignatureType::Poly1271`)
only. Safe, Proxy, and EOA paths are not available. The client does not deploy wallets, batch
unrelated calls, size a merge to `"max"`, or redeem positions automatically.

### Inputs and approvals

Each operation looks up the market on the public CLOB by condition ID and errors if `neg_risk` is
absent. It then selects the canonical pUSD token and the standard or negative-risk collateral
adapter. Callers pass a 0x-prefixed 32-byte condition ID and do not supply contract addresses.

- **Split and merge amounts**: Positive pUSD decimals exactly representable at six decimal places,
  with no rounding. Amounts use pUSD units, never base units; `"max"` is not accepted.
- **Redemption**: No amount argument; redeems both binary index sets `[1, 2]`.

**Approvals are not submitted automatically.** Grant them from the Deposit Wallet before submitting:

| Operation       | Required approval for the market's collateral adapter |
| --------------- | ----------------------------------------------------- |
| Split           | Spend the wallet's pUSD                               |
| Merge or redeem | Act as a Conditional Tokens operator                  |

The standard collateral adapter is absent from the approval plan in `polymarket-set-allowances`; the negative-risk adapter is included. That command signs approvals
from the EOA, so it cannot grant either approval for a Deposit Wallet. Use Polymarket's
[wallet and authentication flow](https://docs.polymarket.com/trading/wallets-auth) to grant the
position-operation approvals from the Deposit Wallet.

### Submit and wait

The following example shows a split submission. Replace the illustrative condition ID with the
market's actual condition ID before running it.

```python
from decimal import Decimal

from nautilus_trader.adapters.polymarket import PolymarketPositionClient

condition_id = "0x" + "11" * 32
client = PolymarketPositionClient()
transaction = await client.split_position(condition_id, Decimal("1"))
outcome = await transaction.wait()
```

`split_position` and `merge_positions` take the condition ID and a positive pUSD `Decimal`.
`redeem_positions` takes only the condition ID. Each method returns a
`PolymarketPositionTransaction` after Relayer submission. Call `wait()` once to poll until a
`PolymarketPositionOutcome` is available. A second `wait()` on the same Python handle raises. The
handle retains its `transaction_id` after waiting starts, including after cancellation or an error.

### Outcomes and errors

| `PolymarketPositionOutcome.status` | Meaning                             |
| ---------------------------------- | ----------------------------------- |
| `confirmed`                        | Relayer reported `STATE_CONFIRMED`. |
| `failed`                           | Relayer reported `STATE_FAILED`.    |
| `invalid`                          | Relayer reported `STATE_INVALID`.   |

Every outcome exposes `transaction_id`. Confirmed and failed outcomes expose `transaction_hash` when the
Relayer supplies one; invalid outcomes always return `None`. Failed and invalid outcomes also expose
`error_msg` when the Relayer supplies one.

#### Submit errors

An HTTP rejection raises before a transaction handle exists. A timed-out submit or a success response
with no `transaction_id` also raises; the on-chain outcome is unknown.

Submit is not retried.

#### Wait timeout

The default is 120 seconds, checked between polling requests. An in-flight request or polling delay can
extend the elapsed time. A timeout raises an error naming the Relayer transaction ID; the terminal state
remains unknown. Python does not expose the Rust wait-timeout or poll-interval setters.

#### Response validation

Relayer redirects are rejected, and polling rejects a response whose transaction ID differs from the
submitted ID.

### Submission coordination

Clients in the same process share submission state for each wallet. A later operation requires a matching
terminal Relayer result and an advanced wallet nonce. A failed or invalid operation that does not advance
the nonce remains blocked.

After signing, the client records a reservation before sending the batch. An HTTP rejection, timeout,
cancellation, or response without a transaction ID then blocks further operations for that wallet, even
if the client is recreated. Failures before that reservation, such as invalid amounts or failed wallet
verification, do not create a new block. An HTTP status alone does not prove that a signed batch cannot
execute.

:::warning
Do not restart and blindly retry an unknown submission. A process restart clears local submission
state but does not cancel a signed transaction. Use a single submitting process per wallet;
coordination does not extend across processes or external wallet tools.
:::

### Recover an unknown submission

#### Retain the submission record

Enable and retain INFO logs before submitting position operations. Before each submit request, the
client emits a `Deposit Wallet submission` record containing:

- Wallet address, reserved nonce, and signed deadline (Unix seconds).
- Operation, target contract, and value.
- Unsigned calldata identifying the condition and, for split or merge, the amount.

The record excludes signatures and credentials, but contains trading intent; restrict access to
retained logs. Keep the transaction ID from the returned handle when available.

:::warning
Logging is not a durable transaction journal. Disabled logging or a process crash can leave no
retained record, preventing safe recovery without further evidence.
:::

#### Reconcile before retrying

1. **Stop submissions.** Include other processes and external wallet tools using the wallet.
1. **Establish whether the operation executed.** Find the submission record and any Relayer
   transaction ID. Match the intended wallet, target, and calldata against finalized on-chain
   transaction receipts and effects. A successful HTTP response, a Relayer failure, or an advanced
   nonce alone does not establish whether the intended operation executed. If it executed, do not
   repeat it.
1. **Keep the wallet blocked while execution remains unknown.** To reconcile by expiry, perform all
   the [expiry checks](#expiry-checks) below. If the nonce advanced, inspect the transaction that
   consumed it instead of assuming failure.
1. **Prove that retrying is safe before restarting.** Establish both that the intended operation did
   not execute and that the original signed batch can no longer execute. If the record is missing,
   the RPC cannot provide consistent finalized state, or contract behavior is unverified, resolve the uncertainty
   with Polymarket before retrying.

#### Expiry checks

The signed deadline is 1,800 seconds after signing by default. Rust callers can change this with
`with_deadline_secs`; longer deadlines delay expiry-based recovery. A `wait()` timeout does not
shorten the signed deadline or cancel the batch.

First verify that the deployed wallet contract enforces the signed deadline and consumes its nonce
when a batch executes. Then read the wallet's `nonce()` with `eth_call` at a finalized Polygon block
and obtain that same block's timestamp. Require both:

- The nonce is unchanged from the reserved nonce.
- The block timestamp is strictly after the signed deadline.

Elapsed local time is insufficient. An advanced nonce requires inspection of the transaction that
consumed it; it does not establish failure.

The client does not perform these recovery checks or release a reservation automatically. Consult
the [Polymarket contract registry](https://docs.polymarket.com/resources/contracts) when identifying
the deployed contracts and the [JSON-RPC reference](https://ethereum.org/en/developers/docs/apis/json-rpc/)
for block-specific calls.

## Configuration

Configure signing and authentication through these parameters or their environment-variable fallbacks:

| Parameter     | Environment variable    | Purpose                                                      |
| ------------- | ----------------------- | ------------------------------------------------------------ |
| `private_key` | `POLYMARKET_PK`         | Wallet key used to sign orders according to `signature_type` |
| `funder`      | `POLYMARKET_FUNDER`     | pUSD funding wallet address                                  |
| `api_key`     | `POLYMARKET_API_KEY`    | CLOB L2 API key                                              |
| `api_secret`  | `POLYMARKET_API_SECRET` | CLOB L2 API secret                                           |
| `passphrase`  | `POLYMARKET_PASSPHRASE` | CLOB L2 API passphrase                                       |

In owner mode, when a parameter is not supplied explicitly, the client reads its environment variable.
Session mode requires explicit credentials and a funder. CLOB L2 credentials authenticate the
private-key signer. For `POLY_1271`, the Deposit Wallet remains the
`funder`; it is not the L2 authentication address.

:::tip
Use environment variables to supply credentials without embedding them in client configuration code.
:::

Instrument loading also has two common controls:

- `auto_load_missing_instruments` (default `True`): Subscribe and request commands for uncached
  instruments trigger an ad-hoc Gamma API load. When disabled, subscribing to an uncached instrument
  returns an error. See [Runtime instrument loading](#runtime-instrument-loading).
- `auto_load_debounce_ms` (default `100`): Window in milliseconds for coalescing concurrent auto-load
  requests into a single batched Gamma call.

## Data capability

Polymarket supports live `L2_MBP` order book deltas, quotes, trades, and resolution
`InstrumentStatus`/`InstrumentClose` events. Instrument definitions are published by bootstrap,
configured refreshes, on-demand loading, single-instrument requests, new-market discovery, and
tick-size changes.

## Orders capability

Polymarket operates as a prediction market with a more limited set of order types and instructions compared to traditional exchanges.

:::tip
For Polymarket live execution, set both the disconnection timeout and post-stop delay to 30
seconds with `with_timeout_disconnection_secs(30)` and `with_delay_post_stop_secs(30)`. The delay
allows residual order and cancellation events to arrive before disconnection, while the timeout
gives each client time to shut down cleanly.
:::

### Order types

| Order Type             | Binary Options | Notes                                                                     |
| ---------------------- | -------------- | ------------------------------------------------------------------------- |
| `MARKET`               | ✓              | **BUY orders require quote quantity**, SELL orders require base quantity. |
| `LIMIT`                | ✓              | BUY orders accept base or quote quantity; SELL orders require base.       |
| `STOP_MARKET`          | -              | *Not supported by Polymarket*.                                            |
| `STOP_LIMIT`           | -              | *Not supported by Polymarket*.                                            |
| `MARKET_IF_TOUCHED`    | -              | *Not supported by Polymarket*.                                            |
| `LIMIT_IF_TOUCHED`     | -              | *Not supported by Polymarket*.                                            |
| `TRAILING_STOP_MARKET` | -              | *Not supported by Polymarket*.                                            |

### Quantity semantics

Polymarket interprets order quantities differently depending on the order type, side, and
`quote_quantity` setting:

- **Limit orders with `quote_quantity=False`** interpret `quantity` as the number of conditional
  tokens (base units).
- **Limit BUY orders with `quote_quantity=True`** interpret `quantity` as pUSD collateral.
- **Market BUY** orders interpret `quantity` as quote notional in **pUSD**.
- **Market SELL** orders use base-unit quantities.

Quote-sized limit SELL orders are not supported. The adapter denies them before submission. It also
denies any limit order whose base or quote quantity truncates to zero at the two-decimal signing
boundary.

#### Limit BUY example

For limit BUY orders with `quote_quantity=True`:

- The adapter truncates collateral to cents, signs it as the maker amount, and derives shares at
  the market's amount precision.
- It updates the local order to the signed share quantity before processing the venue response.
- The signed amounts must preserve the limit price exactly. Otherwise, the adapter rejects the
  order before HTTP. For example, 10.00 pUSD at 0.33 is not representable, while 9.90 pUSD at
  0.33 produces exactly 30 shares.

To cap a limit BUY by collateral, set `quote_quantity=True`:

```python
# Limit BUY with quote quantity (spend $10 pUSD at a limit price of 0.50)
order = strategy.order_factory.limit(
    instrument_id=instrument_id,
    order_side=OrderSide.BUY,
    quantity=instrument.make_qty(10.0),
    price=instrument.make_price(0.50),
    time_in_force=TimeInForce.GTC,
    quote_quantity=True,
)
strategy.submit_order(order)
```

#### Market BUY example

When submitting market BUY orders, set `quote_quantity=True` on the order. The adapter converts
the quote amount (pUSD) to the signed base-unit share amount before posting to the CLOB. The
Polymarket execution client denies base-denominated market buys to
prevent unintended fills. A market BUY submitted with a base-denominated quantity can execute far
more size than intended.

```python
# Market BUY with quote quantity (spend $10 pUSD)
order = strategy.order_factory.market(
    instrument_id=instrument_id,
    order_side=OrderSide.BUY,
    quantity=instrument.make_qty(10.0),
    time_in_force=TimeInForce.IOC,  # Maps to Polymarket FAK
    quote_quantity=True,  # Interpret as pUSD notional
)
strategy.submit_order(order)
```

### Execution instructions

| Instruction   | Binary Options | Notes                                                |
| ------------- | -------------- | ---------------------------------------------------- |
| `post_only`   | ✓              | Supported for limit orders with `GTC` or `GTD` only. |
| `reduce_only` | -              | *Not supported by Polymarket*.                       |

### Time-in-force options

Polymarket calls the `POST /order` field `orderType`. In NautilusTrader, this maps to
`TimeInForce`. The valid combinations depend on the Nautilus order type:

| Nautilus TIF | Polymarket `orderType` | Nautilus order scope | Notes                                                     |
| ------------ | ---------------------- | -------------------- | --------------------------------------------------------- |
| `GTC`        | `GTC`                  | `LIMIT` only         | Good-Til-Cancelled; rests on the book.                    |
| `GTD`        | `GTD`                  | `LIMIT` only         | Good-Til-Date; rests until expiration, fill, or cancel.   |
| `FOK`        | `FOK`                  | `LIMIT` or `MARKET`  | Fill the full size immediately or cancel the whole order. |
| `IOC`        | `FAK`                  | `LIMIT` or `MARKET`  | Fill available size immediately and cancel the remainder. |

Polymarket uses `FAK` (Fill-And-Kill) for the semantics NautilusTrader calls
`IOC` (Immediate or Cancel). Polymarket docs classify `FOK` and `FAK` as market
order types, while `GTC` and `GTD` are limit order types. For Nautilus `MARKET`
orders, the adapter accepts only `IOC` and `FOK`; `GTC` and `GTD` are valid for
resting `LIMIT` orders only.

#### GTD expiry

Set `GTD` expiry at least three minutes after submission. The adapter denies a shorter expiry
before signing. It uses whole Unix seconds and accepts the exact three-minute boundary.

Polymarket reports both a user cancel and a GTD expiry as `CANCELED`. It expires the order one
minute before the supplied expiration, so the minimum effective lifetime is about two minutes. To
request an effective lifetime of N seconds, supply `now + 60 + N`, still subject to the
three-minute minimum.

On the user channel, a `CANCELED` event at or after that one-minute mark becomes `OrderExpired`.
An earlier cancel stays `OrderCanceled`. A REST-recovered cancel also stays `OrderCanceled`. The
open-order payload has an expiration, but no cancel time.

Leave `manage_gtd_expiry` false. The strategy timer fires at the stated expiration and submits a
cancel. It does not emit `OrderExpired`. A user-channel expiry already clears that timer. If the
expiry message was missed, the late cancel does not close the order. Reconciliation still has to.
See [GTD orders](https://docs.polymarket.com/trading/place-orders#limit-orders).

### Minimum order size

Read each market's `min_order_size` from its order book; active markets commonly report five
shares. Marketable orders can also be rejected below **1 pUSD** in notional value with
`invalid amount for a marketable BUY order … min size: $1`. The adapter leaves instrument
`min_quantity` unset because quote-sized BUY quantities use pUSD while base-sized orders use
shares. Submit still sends an undersized order for the venue to reject. Modify checks the cached
share minimum before and after cancel. See [Order modification](#order-modification).

### Advanced order features

| Feature            | Binary Options | Notes                                                   |
| ------------------ | -------------- | ------------------------------------------------------- |
| Order modification | Yes            | Adapter-managed cancel-replace for open `LIMIT` orders. |
| Bracket/OCO orders | -              | *Not supported by Polymarket.*                          |
| Iceberg orders     | -              | *Not supported by Polymarket.*                          |

#### Order modification

Polymarket has no in-place modify endpoint. The execution client cancels the current venue order,
reconciles its final confirmed fills, and signs a replacement for the remaining quantity. The
`ModifyOrder.quantity` value is the absolute target for the logical order, not the replacement leg.
The replacement keeps the `ClientOrderId` and receives a new `VenueOrderId`. The resulting logical
quantity reflects the exact signed base quantity after venue precision normalization, so it can be
slightly lower than the requested target.

The target must leave a replacement of at least one 0.01 lot after the filled quantity and any
quantity voided without reopening. The adapter rejects a modify that does not before it cancels
the venue order. Fills that arrive while the cancel is in flight can still take the replacement
below a lot, in which case the modify is rejected and the order closes as canceled.

When cached instrument `info["min_order_size"]` is a positive decimal string, the signed
replacement share quantity must meet it. For a `FAK` or `FOK` BUY, that quantity is derived from
the cent-truncated pUSD budget. The adapter rejects the modify before it cancels when the signed
quantity is below that minimum, when the stored value is present but not a positive decimal
string, or when a minimum is set but the signed amounts cannot be derived. A missing
`min_order_size` key skips the minimum check. After the cancel, the adapter repeats these checks
on the remaining quantity, using the instrument minimum captured when the modify started, not a
fresh order-book read. An undersized remainder is not posted, the modify is rejected, and the
order closes as canceled. A marketable replacement can still be rejected below 1 pUSD after the
cancel.

The adapter submits no replacement unless the cancel response, canceled order state, and confirmed
trade totals agree. An ambiguous cancel emits `OrderModifyRejected`. An ambiguous replacement stays
in flight under its deterministic signed order hash so a later order update, fill, or order
reconciliation can establish the replacement without emitting a second `OrderAccepted`. Later
modify and cancel commands remain blocked until that happens. This recovery state is not persisted
across an execution-client process restart.

### Batch operations

| Operation    | Binary Options | Notes                                                                                                                               |
| ------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Batch Submit | ✓              | The adapter uses `POST /orders` for independent limit-order batches (max 15 orders per request). See [Batch submit](#batch-submit). |
| Batch Modify | -              | *Not supported by Polymarket*.                                                                                                      |
| Batch Cancel | ✓              | The adapter uses `DELETE /orders`. See [Batch cancel](#batch-cancel).                                                               |

#### Batch submit

`SubmitOrderList` commands are routed to Polymarket's `POST /orders` endpoint. The endpoint
accepts at most 15 orders per request (`BATCH_ORDER_LIMIT`); larger lists are split into
sequential 15-order chunks.

- Only `LIMIT` orders are batched. `MARKET` orders inside the list are routed to the
  single-order path, which signs a marketable order and submits it with `FAK` or `FOK`
  based on Nautilus `time_in_force`.
- `reduce_only` orders, quote-sized SELL orders, and `post_only` with market TIF (`IOC` or `FOK`)
  are denied before submission.
- A single eligible order falls through to `POST /order` so it keeps the single-order retry
  semantics; the batch path deliberately disables retry because the venue does not expose an
  idempotency key.
- If the batch response omits a leg, that order stays submitted for reconciliation. The adapter
  registers the signed order's expected hash so later WebSocket events and cancels still resolve to
  the local order. An omitted response cannot prove that the venue rejected the order.

#### Batch cancel

`BatchCancelOrders` commands with resolved venue order IDs use Polymarket's
[`DELETE /orders`](https://docs.polymarket.com/api-reference/trade/cancel-multiple-orders)
endpoint. The adapter sends sequential chunks and chooses each new chunk from the smaller of the
endpoint's 1,000-ID limit and the signer's current cancellation burst. A signer starts with the
Standard 120-token burst, and a tier reported by one response applies to the next new chunk.

Each chunk retries independently with the same order IDs unless a lower reported tier requires
smaller chunks before the retry. The adapter merges the completed responses and processes each
requested order once after every chunk succeeds. If a later chunk exhausts its retries, earlier
chunks may already have changed venue state, but the adapter emits no partial per-order results;
reconciliation resolves the unknown overall outcome.

In owner mode, without a side filter, `CancelAllOrders` applies to the selected outcome token for the authenticated
execution account, across strategies, even when the local order cache has no matches. The adapter
sends the instrument's raw token ID as `asset_id` to
[`DELETE /cancel-market-orders`](https://docs.polymarket.com/api-reference/trade/cancel-orders-for-a-market).
With a `Buy` or `Sell` filter, or in session mode regardless of the side filter, the adapter
selects matching open orders from the local cache and sends their venue order IDs through
the same chunked `DELETE /orders` path. A matching order that is still awaiting its venue order ID
retains a pending cancellation, which is sent after submission resolves. The ID-based path sends
no cancellation request when the cache has no matching orders.

### Submit response handling

Polymarket's public documentation describes successful
[`POST /order`](https://docs.polymarket.com/api-reference/trade/post-a-new-order) responses
with `success`, `orderID`, `status`, and `errorMsg`, and documents
[API errors](https://docs.polymarket.com/resources/error-codes) as structured error responses.
It does not document statusless client exceptions or transport failures as venue rejections.

#### Successful responses

For a successful response with a non-empty `orderID`, the adapter uses `status` to choose the
initial Nautilus state and whether an order with `FOK` time-in-force needs the five-second REST
check. The venue meanings follow Polymarket's
[order lifecycle](https://docs.polymarket.com/concepts/order-lifecycle).

| Submit `status` | Venue meaning                                | Initial Nautilus state                                         | `FOK` REST check     |
| --------------- | -------------------------------------------- | -------------------------------------------------------------- | -------------------- |
| `live`          | Resting on the book                          | `Accepted`                                                     | Kept                 |
| `matched`       | Matched immediately                          | `Accepted`                                                     | Skipped              |
| `delayed`       | Matching delay in progress                   | `Submitted` until WebSocket or REST activity proves acceptance | Kept                 |
| `unmatched`     | Delay completed without a match; now resting | `Accepted`                                                     | Kept                 |
| Absent or empty | No status supplied                           | `Accepted` unless a proven FOK/FAK error rejects it            | Kept unless rejected |

These meanings apply to the submit response. The adapter treats `delayed` as a submit outcome, not
as a market configuration signal. A `matched` response skips the REST check because the submit
already confirms an immediate match. An absent or empty status emits `OrderAccepted` for
compatibility and keeps the REST check unless a proven unfilled `FOK` or no-match `FAK` response
causes immediate rejection. For a successful response with a non-empty `orderID`, an explicit
`status` takes precedence over an unfilled `FOK` or no-match `FAK` error string.

#### Delayed responses

A `delayed` response:

- Registers the venue order identity and fill tracking immediately and retains them independently of
  bounded replay caches. Later order queries, WebSocket events, and reconciliation reports can then
  resolve the local `ClientOrderId`.
- Leaves the order `Submitted` until a fill, order update, or REST result proves acceptance.
- Emits `OrderAccepted` before any fill, cancellation, expiry, or filled status that proves
  acceptance.
- Resolves an unfilled `FOK` directly as `OrderRejected` when REST returns `UNMATCHED`.

#### Definitive and ambiguous outcomes

Polymarket applies the shared
[command outcome policy](../concepts/execution/policies.md#command-outcomes) and
the adapter guide's
[diagnostic and strategy reason boundary](../developer_guide/adapters.md#separate-diagnostics-from-strategy-facing-reasons)
at its execution boundary.

Ambiguous failures include:

- Transport failures and timeouts.
- Retry exhaustion after an attempt with an unknown outcome.
- Response serialization or decoding failures.
- Local I/O failures.
- Server-side failures.
- HTTP 425 responses.
- HTTP 429 responses that lack CLOB signer-limiter headers.

| Outcome                                                                                   | Nautilus result             | Reason                                    |
| ----------------------------------------------------------------------------------------- | --------------------------- | ----------------------------------------- |
| `success=false`, a documented processing error, or another non-retryable client/API error | `OrderRejected`             | The response proves rejection.            |
| Single or batch `FOK`: `success=true`, no status, and the unfilled error                  | Immediate `OrderRejected`   | The venue proves it killed the order.     |
| Single or batch `IOC`/`FAK`: `success=true`, no status, and the exact no-match error      | Immediate `OrderRejected`   | The venue proves no quantity matched.     |
| Batch leg: `success=true`, empty `orderID`, and a populated `errorMsg`                    | `OrderRejected` with reason | The venue proves it rejected that leg.    |
| No `orderID` and no reason                                                                | Remains `Submitted`         | The response does not prove rejection.    |
| Any ambiguous failure                                                                     | Remains `Submitted`         | The adapter cannot determine the outcome. |
| Definitive retry error after an earlier ambiguous attempt                                 | Remains `Submitted`         | The earlier attempt may have succeeded.   |
| Failure before `POST /order`, such as a failed pUSD balance lookup                        | `OrderDenied`               | The adapter did not submit the order.     |

Local denials format the strategy-facing reason from `OrderDeniedReason`. The leading token is the
stable code, such as `VALIDATION_FAILED` or `UNSUPPORTED_ORDER_TYPE`.

The proven unfilled `FOK` and no-match `FAK` responses resolve as immediate rejections. The `FOK`
response skips the REST check. After an ambiguous single-order attempt, a later HTTP error or
decoded rejection does not prove that the first attempt failed. An accepted response carrying the
matching valid order ID confirms the deterministic signed order; a rejection does not, even with a
matching ID.

#### Error reasons

Diagnostic errors retain the HTTP status and transport or rate-limit context. For venue HTTP status,
rate-limit, and exchange errors, strategy-facing rejection events use the venue reason; other
failures use the bounded error description. The adapter reads the first non-blank string from
`error`, then `errorMsg`, and collapses whitespace and control characters. An empty body becomes
`empty response body`. A plain-text or malformed response uses the same bounded fallback. Invalid
UTF-8 is decoded lossily before that handling. An HTML response uses its title when available, or its
visible text otherwise. Reasons are limited to 512 characters, including the literal
`... [truncated]` truncation marker and its preceding space.

On single and batch submit responses, the exact normalized reason `order_version_mismatch` becomes
`Polymarket CLOB order version mismatch; adapter supports V2 only`. Other submit response reasons
remain unchanged after normalization.

The venue reports a post-only crossing as `invalid post-only order: order crosses book`. Only that
exact normalized reason sets `OrderRejected.due_post_only=true`; other post-only errors remain
ordinary rejections.

#### Retry classification

Retry-managed single-order submit and cancel requests retry HTTP 425, 429, and 5xx responses with the
configured backoff. After retries are exhausted, submit classification is:

| HTTP status                          | Retried | Submit result       | Notes                                                                                                                                |
| ------------------------------------ | ------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| 425                                  | Yes     | Remains `Submitted` | Too Early does not prove rejection.                                                                                                  |
| 429 with CLOB signer-limiter headers | Yes     | `OrderRejected`     | Requires `Poly-RateLimit-Remaining`, `Poly-RateLimit-Reset`, or `Poly-RateLimit-Tier`. An earlier unknown attempt stays `Submitted`. |
| 429 without those headers            | Yes     | Remains `Submitted` | Cloudflare or another hop may have seen the request.                                                                                 |
| 5xx                                  | Yes     | Remains `Submitted` | The command may already have been applied.                                                                                           |
| 400, 401, 403, 404                   | No      | `OrderRejected`     | Non-retryable client or API error.                                                                                                   |

A malformed successful submit response also remains unknown and enters reconciliation instead of
becoming a terminal rejection.

Cancel classification uses the same evidence classes. A non-retryable client or API error after the
cancel is sent, or a local failure that proves the cancel was never transmitted, emits
`OrderCancelRejected`. HTTP 425, headerless 429, and 5xx leave the cancel in flight.

#### Unknown-outcome reconciliation

For an unknown outcome, the adapter:

- Derives the expected Polymarket order hash from the signed EIP-712 order when possible and caches
  it as the `VenueOrderId`. Later WebSocket events and reconciliation reports attach to the local
  `ClientOrderId` instead of becoming external orders.
- Applies the signed quote-to-base quantity update for a quote-quantity market BUY.
- Defers a pending cancel until the expected venue order ID is known.
- Registers fill tracking under that venue order ID.

##### Order and trade reads

The adapter reads the order and its trades from REST, retrying with backoff capped at 30 seconds
until the venue state is known. Terminal trades apply through the settlement records first, then the
order status accepts the order and releases its fills. The status waits while any trade is still
provisional or while confirmed trades do not yet cover the venue's matched quantity.

`QueryOrder` commands made while a signed submission awaits acknowledgement use the same settlement path.
They apply venue trade IDs directly, so buffered WebSocket trades and the later submit response do
not repeat acceptance or count the same fill twice.

##### Report gating

Reports that cover the order or its instrument fail until that read applies or reading stops, so
reconciliation does not infer fills from partial venue state.

##### Read timeout

Reading stops after 10 minutes without applying the venue state. The adapter logs a warning, lifts
the report gate, and leaves the order `Submitted`.

If the local order closes before recovery completes, the adapter cancels any venue order still
reported as live or delayed. Recovery then requires a terminal order read and trade evidence covering
its matched quantity. Cancellation alone does not establish that no fill occurred. Failed or empty
reads and the 10-minute timeout do not end this recovery, even if a late fill reopens the local order.
If the venue keeps returning an empty order lookup, the report gate remains closed indefinitely for
that order, its instrument, and account-wide reports. Fills that the engine cannot apply remain
visible through the settlement report gate.

### Position management

| Feature              | Binary Options | Notes                                                                       |
| -------------------- | -------------- | --------------------------------------------------------------------------- |
| Query positions      | ✓              | Data API user positions, excluding resolved balances.                       |
| Split, merge, redeem | ✓              | Deposit Wallet operations; see [Position operations](#position-operations). |
| Position mode        | -              | Binary outcome positions only.                                              |
| Leverage control     | -              | No leverage available.                                                      |
| Margin mode          | -              | No margin trading.                                                          |

### Order querying

| Feature              | Binary Options | Notes                          |
| -------------------- | -------------- | ------------------------------ |
| Query open orders    | ✓              | Active orders only.            |
| Query order history  | ✓              | Limited historical data.       |
| Order status updates | ✓              | Real-time order state changes. |

Shown in full with attribution under the source's licence. Licence: LGPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.