# Polymarket Trading Guide
This document provides a comprehensive guide for developers on how to execute trades on Polymarket's CLOB (Central Limit Order Book) using the `polysqueeze` Rust client. It covers setup, authentication, real-time data streaming via WebSockets, and core trading methods.
## 1. Trading Execution
This section details the process of setting up the client, connecting to the real-time data stream, and executing trades.
### 1.1. Setup & Authentication
Before you can trade, you need to initialize the client and authenticate with Polymarket's API. The client supports two levels of authentication:
* **L1 Auth (Private Key)**: Used for initial authentication and for creating or deriving API keys. It signs a message with your wallet's private key.
* **L2 Auth (API Key)**: Used for all subsequent trading activities like placing or canceling orders. It uses an HMAC signature derived from your API secret.
First, you need a `ClobClient` instance.
**File**: `src/client.rs`
```rust
use polysqueeze::client::ClobClient;
use polysqueeze::types::ApiCredentials;
// The base URL for the Polymarket CLOB API
const API_URL: &str = "https://clob.polymarket.com";
const POLYGON_CHAIN_ID: u64 = 137;
// Your wallet's private key (keep this secure!)
let private_key = "0x...";
// Initialize a client with L1 authentication capabilities
let client = ClobClient::with_l1_headers(API_URL, &private_key, POLYGON_CHAIN_ID);
```
With an L1-authenticated client, you can create or derive your L2 API credentials.
```rust
// It's recommended to use create_or_derive_api_key, which attempts to create a new key
// and falls back to deriving an existing one if creation fails.
let api_creds: ApiCredentials = client.create_or_derive_api_key(None).await?;
// Now, create a new client configured for L2 trading operations
let trading_client = ClobClient::with_l2_headers(API_URL, &private_key, POLYGON_CHAIN_ID, api_creds);
// You can verify the connection is working
let is_ok = trading_client.get_ok().await;
println!("Client connection status: {}", if is_ok { "OK" } else { "Failed" });
// If you want orders to be posted from a different profile/funder address than your wallet,
// override it before sending orders:
trading_client
.set_funder("0x5aa8c593514f9edf8c92f8eb020bc40b4eb2f49b")?;
```
### 1.2. Using the WebSocket for Real-time Data
For low-latency trading, you must use the WebSocket stream to receive real-time order book updates and trade notifications.
**File**: `src/ws.rs`
The `WebSocketStream` provides the connection, and you subscribe to specific channels (`USER` or `MARKET`) to get data.
* **`USER` Channel**: Provides updates on your own orders and fills.
* **`MARKET` Channel**: Provides public market data, like order book updates (`BookUpdate`) and public trades (`Trade`).
**Example: Connecting and Subscribing**
```rust
use polysqueeze::ws::WebSocketStream;
use polysqueeze::types::{WssAuth, StreamMessage};
use futures::stream::StreamExt;
// 1. Generate authentication details for the WebSocket connection.
// This requires signing a message with your private key.
// (See `src/auth.rs` for `sign_clob_auth_message`)
let auth_details: WssAuth = ...; // Assume you have generated this
// 2. Initialize the WebSocket stream
let wss_url = "wss://clob.polymarket.com/ws";
let mut stream = WebSocketStream::new(wss_url).with_auth(auth_details);
// 3. Connect to the WebSocket
stream.connect().await?;
// 4. Subscribe to channels.
// Subscribe to your user-specific updates for a given market (by condition_id)
stream.subscribe_user_channel(vec!["0x...market_condition_id...".to_string()]).await?;
// Subscribe to public market data for a specific token
stream.subscribe_market_channel(vec!["123456789...token_id...".to_string()]).await?;
// 5. Process incoming messages
while let Some(message_result) = stream.next().await {
match message_result {
Ok(message) => {
match message {
StreamMessage::BookUpdate { data } => {
println!("Received book update for token {}: {} {} @ {}",
data.token_id, data.side.as_str(), data.size, data.price);
// Your logic to update your local order book would go here.
},
StreamMessage::Trade { data } => {
println!("New trade executed for token {}: {} @ {}",
data.token_id, data.size, data.price);
},
StreamMessage::UserOrderUpdate { data } => {
println!("Your order {} was updated. Status: {:?}", data.id, data.status);
},
StreamMessage::Heartbeat { timestamp } => {
println!("Received heartbeat at {}", timestamp);
}
_ => { /* Handle other message types */ }
}
},
Err(e) => {
eprintln!("Stream error: {}", e);
// Implement reconnection logic if necessary
}
}
}
```
### 1.3. Core Trading Methods
All trading actions are performed using the `ClobClient`.
#### Creating and Posting an Order
Placing an order is a two-step process:
1. `create_order`: Creates and signs the order payload locally. This does **not** send the order to the exchange.
2. `post_order`: Submits the signed payload to the exchange.
**File**: `src/client.rs`, `src/orders.rs`
```rust
use polysqueeze::client::{ClobClient, OrderArgs};
use polysqueeze::types::{Side, OrderType};
use rust_decimal_macros::dec;
// Assume `trading_client` is an L2-authenticated ClobClient instance
let trading_client: ClobClient = ...;
// 1. Define the order parameters
let order_args = OrderArgs {
token_id: "123456789...".to_string(), // The token ID for the outcome you want to trade
price: dec!(0.65), // The price you want to trade at ($0.65)
size: dec!(100), // The number of shares
side: Side::BUY, // BUY or SELL
};
// 2. Create and sign the order.
// This happens locally and does not involve a network request.
// It requires market context like tick_size and neg_risk, which the client fetches internally.
let signed_order_request = trading_client.create_order(&order_args, None, None, None).await?;
// 3. Post the signed order to the exchange.
// This is the network request that places the order on the book.
let post_response = trading_client.post_order(signed_order_request, OrderType::GTC).await?;
println!("Order posted successfully: {:?}", post_response);
```
#### Canceling Orders
You can cancel a single order, multiple orders, or all open orders.
**File**: `src/client.rs`
```rust
// Cancel a single order by its ID
let order_id_to_cancel = "...";
let cancel_response = trading_client.cancel(order_id_to_cancel).await?;
println!("Cancel response: {:?}", cancel_response);
// Cancel all open orders
let cancel_all_response = trading_client.cancel_all().await?;
println!("Cancel all response: {:?}", cancel_all_response);
```
#### Fetching Account State
You can query your open orders and historical trades.
**File**: `src/client.rs`
```rust
// Get all open orders
let open_orders = trading_client.get_orders(None, None).await?;
println!("You have {} open orders.", open_orders.len());
// Get trade history
let trade_history = trading_client.get_trades(None, None).await?;
println!("Fetched {} pages of trade history.", trade_history.len());
```
## 2. Understanding Polymarket Markets
Polymarket uses two primary market structures. The structure determines which smart contracts are used for settlement and has implications for capital efficiency. The `polysqueeze` client handles this distinction automatically when creating orders, provided it can fetch the `neg_risk` status for the market.
### 2.1. CTF (Conditional Tokens Framework)
This is the standard market type on Polymarket, based on the Gnosis Conditional Tokens Framework.
- **Functionality**: CTF markets are used for questions with two or more distinct, mutually exclusive outcomes. For example, a "Yes" or "No" market, or a market on "Who will win the election?" with multiple candidates.
- **Mechanism**: It works by creating "conditional tokens" (outcome tokens) that are backed by a collateral asset (like USDC). If you buy a "Yes" share for $0.65, you are paying $0.65 USDC, and you will receive $1 USDC if the outcome is "Yes", and $0 otherwise. The price of an outcome token reflects the market's perceived probability of that outcome occurring.
### 2.2. NegRisk (Negative Risk) Markets
NegRisk markets are a Polymarket-specific innovation designed for capital efficiency in "winner-take-all" scenarios.
- **Functionality**: A NegRisk market is a group of related binary (Yes/No) markets where only one outcome can be true. For example, a series of markets for an election: "Will Candidate A win?", "Will Candidate B win?", etc. If one resolves to "Yes", all others must resolve to "No".
- **Mechanism**: The key feature is that holding a "No" share in one market of the group is economically equivalent to holding "Yes" shares in all other markets of that same group. The system allows for this conversion, making it more capital-efficient to bet against a single outcome in a large field.
- **In `polysqueeze`**: The `neg_risk` flag in `get_contract_config` (`src/config.rs`) and `create_order` (`src/orders.rs`) directs the client to use a different set of smart contract addresses for trading in NegRisk markets. This ensures the order is signed correctly for the specific market type.