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

# Trading and Fees

> Set up a wallet, place and manage Polymarket V2 orders, and understand protocol fees, builder attribution, and optional application fees.

The SDK trading modules provide a common workflow for wallet setup, CLOB credentials, approvals, order placement, cancellation, and position operations. Polymarket V2 is the default in every current PolyNode SDK.

<Warning>
  Trading methods sign messages and can place orders or submit on-chain transactions. Start with a dedicated test wallet, confirm the token ID, price, size, and funder address, and never commit private keys or CLOB credentials.
</Warning>

## Install

<CodeGroup>
  ```bash TypeScript theme={null}
  npm install polynode-sdk@^0.12.2 viem better-sqlite3 \
    @polymarket/clob-client \
    @polymarket/builder-relayer-client \
    @polymarket/builder-signing-sdk
  ```

  ```bash Python theme={null}
  python -m pip install "polynode[trading]>=0.12.2,<0.13"
  ```

  ```toml Rust theme={null}
  [dependencies]
  polynode = { version = "0.15.2", features = ["trading"] }
  tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
  ```
</CodeGroup>

Store credentials outside source control:

```bash theme={null}
export POLYNODE_API_KEY="pn_live_..."
export POLYGON_PRIVATE_KEY="0x..."
```

## Current V2 behavior

You do not need to opt into V2. These are the defaults:

| SDK        | Default                 |
| ---------- | ----------------------- |
| TypeScript | `exchangeVersion: 'v2'` |
| Python     | `ExchangeVersion.V2`    |
| Rust       | `ExchangeVersion::V2`   |

V2 uses PolyUSD collateral and applies Polymarket protocol fees when an eligible taker order matches. V2 signed orders do **not** include the legacy `feeRateBps`, `nonce`, or `taker` fields. The SDK omits those fields from the V2 signature and wire payload.

The explicit V1 compatibility mode is different: its signed order includes the market fee rate. When V1 is selected, the SDK fetches the current fee and refuses to sign if fee, tick-size, or negative-risk metadata cannot be resolved safely.

<Note>
  Do not copy V1 fields into a V2 order. If you do not have a deliberate legacy requirement, keep the V2 default.
</Note>

## Set up a wallet

`ensureReady` / `ensure_ready` is idempotent. It discovers the wallet type, creates or loads CLOB credentials, and completes required setup. For a new wallet it can deploy contracts or submit approvals, so treat it as a state-changing operation.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { PolyNodeTrader } from 'polynode-sdk';

  const trader = new PolyNodeTrader({
    polynodeKey: process.env.POLYNODE_API_KEY!,
    exchangeVersion: 'v2', // Optional: V2 is already the default
  });

  const ready = await trader.ensureReady(process.env.POLYGON_PRIVATE_KEY!);
  console.log({
    wallet: ready.wallet,
    funderAddress: ready.funderAddress,
    approvalsSet: ready.approvalsSet,
  });
  ```

  ```python Python theme={null}
  import os
  from polynode.trading import ExchangeVersion, PolyNodeTrader, TraderConfig

  trader = PolyNodeTrader(TraderConfig(
      polynode_key=os.environ["POLYNODE_API_KEY"],
      exchange_version=ExchangeVersion.V2,  # Optional: already the default
  ))

  ready = await trader.ensure_ready(os.environ["POLYGON_PRIVATE_KEY"])
  print({
      "wallet": ready.wallet,
      "funder_address": ready.funder_address,
      "approvals_set": ready.approvals_set,
  })
  ```

  ```rust Rust theme={null}
  use polynode::trading::{
      ExchangeVersion, PolyNodeTrader, PrivateKeySigner, TraderConfig,
  };

  let mut trader = PolyNodeTrader::new(TraderConfig {
      polynode_key: std::env::var("POLYNODE_API_KEY")?,
      exchange_version: ExchangeVersion::V2, // Optional: already the default
      ..Default::default()
  })?;

  let signer = PrivateKeySigner::from_hex(&std::env::var("POLYGON_PRIVATE_KEY")?)?;
  let ready = trader.ensure_ready(Box::new(signer), None).await?;
  println!("fund this address: {}", ready.funder_address);
  ```
</CodeGroup>

Fund the returned `funderAddress` / `funder_address`, not an address guessed from the signing key. The correct funding flow depends on the wallet type. See [Deposit wallets](/guides/deposit-wallets), [V2 details](/guides/v2-details), and [PolyUSD](/guides/polyusd).

## Place an order

The following snippets place a real order when run. Replace every placeholder and validate the market first.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const result = await trader.order({
    tokenId: process.env.POLYMARKET_TOKEN_ID!,
    side: 'BUY',
    price: 0.55,
    size: 10,
    type: 'GTC',
    postOnly: true,
  });

  if (!result.success) throw new Error(result.error ?? 'Order rejected');
  console.log(result.orderId);
  ```

  ```python Python theme={null}
  import os
  from polynode.trading import OrderParams

  result = await trader.order(OrderParams(
      token_id=os.environ["POLYMARKET_TOKEN_ID"],
      side="BUY",
      price=0.55,
      size=10,
      type="GTC",
      post_only=True,
  ))

  if not result.success:
      raise RuntimeError(result.error or "Order rejected")
  print(result.order_id)
  ```

  ```rust Rust theme={null}
  use polynode::trading::{OrderParams, OrderSide, OrderType};

  let result = trader.order(OrderParams {
      token_id: std::env::var("POLYMARKET_TOKEN_ID")?,
      side: OrderSide::Buy,
      price: 0.55,
      size: 10.0,
      order_type: OrderType::GTC,
      expiration: None,
      post_only: true,
      fee_config: None,
      builder: None,
  }).await?;

  if !result.success {
      return Err(polynode::Error::Trading(
          result.error.unwrap_or_else(|| "order rejected".into()),
      ));
  }
  println!("{:?}", result.order_id);
  ```
</CodeGroup>

Supported time-in-force values are `GTC`, `GTD`, `FOK`, and `FAK`. A V2 `GTD` expiration must include the required safety buffer; the SDK rejects an invalid value before submission.

## Cancel and inspect orders

Each SDK supports:

* canceling one order;
* canceling all active orders;
* canceling active orders for a market;
* listing open orders;
* checking balances and approvals;
* reading the local order-attempt history.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const open = await trader.getOpenOrders();
  if (open[0]) await trader.cancelOrder(open[0].id);
  ```

  ```python Python theme={null}
  open_orders = await trader.get_open_orders()
  if open_orders:
      await trader.cancel_order(open_orders[0].id)
  ```

  ```rust Rust theme={null}
  let open_orders = trader.get_open_orders().await?;
  if let Some(order) = open_orders.first() {
      trader.cancel_order(&order.id).await?;
  }
  ```
</CodeGroup>

## Three different fee concepts

Keep these separate in your product and accounting:

| Concept                  | What it means                                                                 | How it is configured                                                           |
| ------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Polymarket protocol fee  | A market fee applied by Polymarket when eligible V2 liquidity matches         | Not a signed V2 order field; inspect market metadata for display and budgeting |
| Builder attribution      | Associates eligible volume with a public builder code                         | `builderCode` / `builder_code`, or the per-order `builder` override            |
| Optional application fee | A fee your application deliberately charges through the SDK fee configuration | `feeConfig` / `fee_config`; disabled unless configured                         |

Builder attribution does not by itself charge the user an application fee. An application fee is enabled only when you explicitly configure it. See [Fee escrow](/guides/fee-escrow) before enabling application fees.

## Builder attribution

Current V2 orders use a public `bytes32` builder code. The SDK supplies PolyNode's public code by default. You can provide your own code at trader level, override it for one order, or disable the default attribution.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const trader = new PolyNodeTrader({
    polynodeKey: process.env.POLYNODE_API_KEY!,
    builderCode: process.env.POLYMARKET_BUILDER_CODE!,
  });

  // Per-order override:
  await trader.order({ ...order, builder: anotherBuilderCode });
  ```

  ```python Python theme={null}
  trader = PolyNodeTrader(TraderConfig(
      polynode_key=os.environ["POLYNODE_API_KEY"],
      builder_code=os.environ["POLYMARKET_BUILDER_CODE"],
  ))

  # Per-order override:
  await trader.order(OrderParams(**order_values, builder=another_builder_code))
  ```

  ```rust Rust theme={null}
  let mut trader = PolyNodeTrader::new(TraderConfig {
      polynode_key: std::env::var("POLYNODE_API_KEY")?,
      builder_code: Some(std::env::var("POLYMARKET_BUILDER_CODE")?),
      ..Default::default()
  })?;

  // `OrderParams.builder` overrides the config for one order.
  ```
</CodeGroup>

Generate and manage builder credentials in your Polymarket builder settings. Builder credentials are secrets; the public builder code is not.

## Position operations

The trading modules also expose split, merge, and negative-risk conversion builders or helpers. These are on-chain operations and are distinct from placing a CLOB order. Review [Position management](/guides/position-management) before signing or submitting one.

## Cleanup

Close the trader to flush and release its local storage:

<CodeGroup>
  ```typescript TypeScript theme={null}
  trader.close();
  ```

  ```python Python theme={null}
  trader.close()
  ```

  ```rust Rust theme={null}
  trader.close();
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Find a token with V3" icon="magnifying-glass" href="/sdks/v3-api">
    Discover markets and token IDs before constructing an order.
  </Card>

  <Card title="Stream a verified book" icon="book-open" href="/sdks/orderbook">
    Use PN1 integrity before making price-sensitive decisions.
  </Card>

  <Card title="V2 migration details" icon="arrows-rotate" href="/guides/v2-migration">
    Understand the current exchange and payload differences.
  </Card>

  <Card title="Trading errors" icon="triangle-exclamation" href="/sdks/ts/errors">
    Handle rejected orders and transport failures explicitly.
  </Card>
</CardGroup>
