Read-only Polymarket data-API trades client (TradesClient) added to the wx-market crate for fetching per-token trade history with automatic pagination and retry.
Location
- File:
crates/wx-market/src/trades.rs - Module:
pub mod tradesincrates/wx-market/src/lib.rs - Branch:
research/intraday-drift
Public API
Side
pub enum Side { Buy, Sell }#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Deserialized from the "side" field using eq_ignore_ascii_case("SELL") — anything that is not "SELL" (case-insensitive) maps to Buy.
Trade
pub struct Trade {
pub t: i64,
pub price: f64,
pub size: f64,
pub side: Side,
}t is a Unix timestamp in seconds. price and size are floating-point; size is quantity in shares, not dollars.
TradesClient
Constructor takes any Into<String> base URL — the same pattern used by ClobPriceHistory, which enables wiremock injection in tests:
pub fn new(base: impl Into<String>) -> Selfpage — single page fetch
pub async fn page(
&self,
asset: &str,
limit: u32,
offset: u32,
) -> Result<Vec<Trade>>GET {base}/trades?asset=<asset>&limit=<limit>&offset=<offset>
Retries up to 3 times with linear backoff: 200ms * attempt (200ms, 400ms, 600ms).
all_by_asset — full paginated fetch
pub async fn all_by_asset(&self, asset: &str) -> Result<Vec<Trade>>Paginates automatically with PAGE = 500 items per request. Stops when either:
- A partial page is returned (
n < PAGE), indicating the last page, or - The accumulated count reaches
MAX_TRADES_PER_TOKEN = 20_000
Endpoint & Response Shape
- Base URL (production):
https://data-api.polymarket.com - Path:
/trades - Key query params:
asset= YES-token CLOB token ID,limit,offset - Response JSON: a JSON array of trade objects
Different base URL from CLOB
The trades endpoint lives at
data-api.polymarket.com, NOTclob.polymarket.com. The price-history endpoint (ClobPriceHistory) uses the CLOB base. Mixing them causes 404s.
asset param
Like
ClobPriceHistory, theassetparam must be the YES-token CLOB token ID (clobTokenIds[0]from the Gamma API), not the condition ID.
Internal: RawTrade and de_flex_i64
RawTrade is a private deserialization struct that feeds into Trade. The timestamp field uses a custom deserializer de_flex_i64 that accepts the value as either a JSON number or a JSON string:
fn de_flex_i64<'de, D: Deserializer<'de>>(d: D) -> std::result::Result<i64, D::Error>This handles observed (and potential future) API drift where the t field is emitted as "1234567890" (string) instead of 1234567890 (number). All other RawTrade fields are straightforwardly typed.
Why
de_flex_i64?The Polymarket data-API has returned timestamps as strings in some responses. Rather than crashing at the deserialize step, the flex deserializer future-proofs the client against silent API format changes.
Retry Behaviour
Same pattern as ClobPriceHistory / clob.rs:
- Up to
MAX_RETRIES = 3attempts - Linear backoff:
200ms * attempt(200, 400, 600ms) - On HTTP send error →
Error::Http(String) - On JSON deserialisation failure →
Error::Parse(String)
Error Mapping
| Failure mode | Error variant |
|---|---|
| HTTP send / network error | wx_core::error::Error::Http(String) |
| JSON deserialisation failure | wx_core::error::Error::Parse(String) |
Testing
- Uses
wiremockMockServer— same dev-dep already present inwx-market - Test confirms mock server wiring, response parsing,
Sidedeserialization,de_flex_i64flexibility, and field-level accuracy - All tests passing
Pagination Constants
| Constant | Value | Purpose |
|---|---|---|
PAGE | 500 | Items per request |
MAX_TRADES_PER_TOKEN | 20 000 | Hard ceiling to prevent runaway fetches |
Data Flow
graph LR Caller["Research pipeline"] -->|"all_by_asset(token_id)"| TC["TradesClient"] TC -->|"page(asset, 500, offset) × N"| DAPI["data-api.polymarket.com"] DAPI -->|"[{t,price,size,side}]"| TC TC -->|"retry 3x / 200ms linear"| DAPI TC -->|"Vec<Trade>"| Caller style TC fill:#264653,stroke:#2a9d8f,color:#fff style DAPI fill:#2d2d2d,stroke:#888,color:#fff
Design Notes
TradesClientis read-only — no write methods, no mutable state beyond the innerreqwest::Client- Constructor pattern (
new(base)) matchesClobPriceHistoryexactly — both clients can be constructed for the same wiremock server in integration tests RawTradeis private — callers only ever seeVec<Trade>MAX_TRADES_PER_TOKENcap prevents a runaway loop on a heavily-traded token; callpage()directly if you need more control
Related
- wx-market-prices-history — sibling client in the same crate; CLOB price-history, same retry pattern
- polymarket-weather-market-structure — CLOB token IDs, market structure, data-API endpoint notes
- wx-research-crate — consumer crate that will use
TradesClientfor intraday-drift research - weather-bet — project overview