polyfill-rs 0.1.0

High-performance drop-in replacement for polymarket-rs-client with HFT optimizations
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# polyfill-rs

[![Crates.io](https://img.shields.io/crates/v/polyfill-rs.svg)](https://crates.io/crates/polyfill-rs)
[![Documentation](https://docs.rs/polyfill-rs/badge.svg)](https://docs.rs/polyfill-rs)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE)

A high-performance, drop-in replacement for `polymarket-rs-client` optimized for high-frequency trading.

## Quick Start

Add to your `Cargo.toml`:

```toml
[dependencies]
polyfill-rs = "0.1.0"
```

Replace your imports:

```rust
// Before: use polymarket_rs_client::{ClobClient, Side, OrderType};
use polyfill_rs::{ClobClient, Side, OrderType};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = ClobClient::new("https://clob.polymarket.com");
    let markets = client.get_sampling_markets(None).await?;
    println!("Found {} markets", markets.data.len());
    Ok(())
}
```

**That's it!** Your existing code works unchanged, but now runs significantly faster.

## Why polyfill-rs?

**🔄 100% API Compatible**: Drop-in replacement for `polymarket-rs-client` with identical method signatures

**🚀 Performance Optimized**: Fixed-point arithmetic and zero-allocation hot paths for HFT environments

**📈 Production Ready**: Used in live trading environments processing thousands of updates per second

**🛠️ Enhanced Features**: WebSocket streaming, advanced fill processing, and comprehensive metrics

## Distribution & Usage

### Crates.io Publication
Once published to [crates.io](https://crates.io), users can add polyfill-rs to their projects with a simple dependency declaration. Documentation is automatically hosted on [docs.rs](https://docs.rs/polyfill-rs).

### Migration from polymarket-rs-client
See our [Migration Guide](./MIGRATION_GUIDE.md) for detailed instructions. The process is typically:

1. Update dependency in `Cargo.toml`
2. Change import statements
3. Enjoy improved performance with zero code changes

### Usage Patterns

**Basic Trading Bot:**
```rust
use polyfill_rs::{ClobClient, OrderArgs, Side, OrderType};
use rust_decimal_macros::dec;

let client = ClobClient::with_l2_headers(host, private_key, chain_id, api_creds);

// Create and submit order
let order_args = OrderArgs::new("token_id", dec!(0.75), dec!(100.0), Side::BUY);
let result = client.create_and_post_order(&order_args).await?;
```

**High-Frequency Market Making:**
```rust
use polyfill_rs::{OrderBookImpl, WebSocketStream};

// Real-time order book with fixed-point optimizations
let mut book = OrderBookImpl::new("token_id".to_string(), 100);
let mut stream = WebSocketStream::new("wss://ws-subscriptions-clob.polymarket.com").await?;

// Process thousands of updates per second
while let Some(update) = stream.next().await {
    book.apply_delta_fast(&update.into())?;
    let spread = book.spread_fast(); // Returns in ticks for maximum speed
}
```

## How It Works

The library has four main pieces that work together:

### Order Book Engine
This is where the magic happens. Instead of using slow decimal math like everyone else, we use fixed-point integers internally:

- **Before**: `BTreeMap<Decimal, Decimal>` (slow decimal operations + allocations)
- **After**: `BTreeMap<u32, i64>` (fast integer operations, zero allocations)

The order book can process updates much faster because integer comparisons are fundamentally faster than decimal ones. We only convert back to decimals when you actually need the data.

*Want to see how this works?* Check out `src/book.rs` - every optimization has commented-out "before" code so you can see exactly what changed and why.

### Trade Execution Simulator
Want to know what would happen if you bought 1000 tokens right now? This simulates walking through the order book levels:

```rust
let impact = book.calculate_market_impact(Side::BUY, Decimal::from(1000));
// Tells you: average price, total cost, market impact percentage
```

It's smart about slippage protection and won't let you accidentally market-buy at ridiculous prices.

### Real-Time Data Streaming
WebSocket connections that don't give up. When the connection drops (and it will), the library automatically reconnects with exponential backoff. No more babysitting your data feeds.

### HTTP Client
All the boring stuff like authentication, rate limiting, and retry logic. It just works so you don't have to think about it.

## Performance (Benchmarks Coming Soon)

The library is designed around several key optimizations:

### Order Book Operations
- **Fixed-point math**: Integer operations instead of decimal arithmetic
- **Zero allocations**: Reuse data structures in hot paths
- **Efficient lookups**: Optimized data structures for common operations
- **Batch processing**: Handle multiple updates efficiently

### Memory Efficiency
- **Compact representations**: Smaller memory footprint per price level
- **Controlled depth**: Only track relevant price levels
- **Smart cleanup**: Remove stale data automatically

### Design Philosophy
The core insight is that most trading operations don't need full decimal precision during intermediate calculations. By using fixed-point integers internally and only converting to decimals at the API boundaries, we can:

- Eliminate allocation overhead in hot paths
- Use faster integer arithmetic
- Reduce memory usage significantly
- Maintain full precision where it matters

**Learning from the code**: The performance optimizations are documented with detailed comments explaining the math, memory layout, and algorithmic choices. It's like a mini-course in high-frequency trading optimization.

## Getting Started

```toml
[dependencies]
polyfill-rs = "0.1.0"
```

## Basic Usage

### If You're Coming From polymarket-rs-client

Good news: your existing code should work without changes. I kept the same API.

```rust
use polyfill_rs::{ClobClient, OrderArgs, Side};
use rust_decimal::Decimal;

// Same initialization as before
let mut client = ClobClient::with_l1_headers(
    "https://clob.polymarket.com",
    "your_private_key",
    137,
);

// Same API calls
let api_creds = client.create_or_derive_api_key(None).await?;
client.set_api_creds(api_creds);

// Same order creation
let order_args = OrderArgs::new(
    "token_id",
    Decimal::from_str("0.75")?,
    Decimal::from_str("100.0")?,
    Side::BUY,
);

let result = client.create_and_post_order(&order_args).await?;
```

The difference is that this now runs way faster under the hood.

### Real-Time Order Book Tracking

Here's where it gets interesting. You can track live order books for multiple tokens:

```rust
use polyfill_rs::{OrderBookManager, OrderDelta, Side};

let mut book_manager = OrderBookManager::new(50); // Keep top 50 price levels

// This is what happens when you get a WebSocket update
let delta = OrderDelta {
    token_id: "market_token".to_string(),
    timestamp: chrono::Utc::now(),
    side: Side::BUY,
    price: Decimal::from_str("0.75")?,
    size: Decimal::from_str("100.0")?,  // 0 means remove this price level
    sequence: 1,
};

book_manager.apply_delta(delta)?;  // This is now super fast

// Get current market state
let book = book_manager.get_book("market_token")?;
let spread = book.spread();           // How tight is the market?
let mid_price = book.mid_price();     // Fair value estimate
let best_bid = book.best_bid();       // Highest buy price
let best_ask = book.best_ask();       // Lowest sell price
```

The `apply_delta` call used to be the bottleneck. Now it's basically free.

### Market Impact Analysis

Before you place a big order, you probably want to know what it'll cost you:

```rust
use polyfill_rs::FillEngine;

let mut fill_engine = FillEngine::new(
    Decimal::from_str("0.001")?, // max slippage: 0.1%
    Decimal::from_str("0.02")?,  // fee rate: 2%
    10,                          // fee in basis points
);

// Simulate buying $1000 worth
let order = MarketOrderRequest {
    token_id: "market_token".to_string(),
    side: Side::BUY,
    amount: Decimal::from_str("1000.0")?,
    slippage_tolerance: Some(Decimal::from_str("0.005")?), // 0.5%
    client_id: None,
};

let result = fill_engine.execute_market_order(&order, &book)?;

println!("If you bought $1000 worth right now:");
println!("- Average price: ${}", result.average_price);
println!("- Total tokens: {}", result.total_size);
println!("- Fees: ${}", result.fees);
println!("- Market impact: {}%", result.impact_pct * 100);
```

This tells you exactly what would happen without actually placing the order. Super useful for position sizing.

### WebSocket Streaming (The Fun Part)

Here's how you connect to live market data. The library handles all the annoying reconnection stuff:

```rust
use polyfill_rs::{WebSocketStream, StreamManager};

let mut stream = WebSocketStream::new("wss://clob.polymarket.com/ws");

// Set up authentication (you'll need API credentials)
let auth = WssAuth {
    address: "your_eth_address".to_string(),
    signature: "your_signature".to_string(),
    timestamp: chrono::Utc::now().timestamp() as u64,
    nonce: "random_nonce".to_string(),
};
stream = stream.with_auth(auth);

// Subscribe to specific markets
stream.subscribe_market_channel(vec!["token_id_1".to_string(), "token_id_2".to_string()]).await?;

// Process live updates
while let Some(message) = stream.next().await {
    match message? {
        StreamMessage::MarketBookUpdate { data } => {
            // This is where the fast order book updates happen
            book_manager.apply_delta_fast(data)?;
        }
        StreamMessage::MarketTrade { data } => {
            println!("Trade: {} tokens at ${}", data.size, data.price);
        }
        StreamMessage::Heartbeat { .. } => {
            // Connection is alive
        }
        _ => {}
    }
}
```

The stream automatically reconnects when it drops. You just keep processing messages.

### Example: Simple Spread Trading Bot

Here's a basic bot that looks for wide spreads and tries to capture them:

```rust
use polyfill_rs::{ClobClient, OrderBookManager, FillEngine};

struct SpreadBot {
    client: ClobClient,
    book_manager: OrderBookManager,
    min_spread_pct: Decimal,  // Only trade if spread > this %
    position_size: Decimal,   // How much to trade each time
}

impl SpreadBot {
    async fn check_opportunity(&mut self, token_id: &str) -> Result<bool> {
        let book = self.book_manager.get_book(token_id)?;
        
        // Get current market state
        let spread_pct = book.spread_pct().unwrap_or_default();
        let best_bid = book.best_bid();
        let best_ask = book.best_ask();
        
        // Only trade if spread is wide enough and we have liquidity
        if spread_pct > self.min_spread_pct && best_bid.is_some() && best_ask.is_some() {
            println!("Found opportunity: {}% spread on {}", spread_pct, token_id);
            
            // Check if our order size would move the market too much
            let impact = book.calculate_market_impact(Side::BUY, self.position_size);
            if let Some(impact) = impact {
                if impact.impact_pct < Decimal::from_str("0.01")? { // < 1% impact
                    return Ok(true);
                }
            }
        }
        
        Ok(false)
    }
    
    async fn execute_trade(&mut self, token_id: &str) -> Result<()> {
        // This is where you'd actually place orders
        // Left as an exercise for the reader :)
        println!("Would place orders for {}", token_id);
        Ok(())
    }
}
```

The key insight: with fast order book updates, you can check hundreds of tokens for opportunities without the library being the bottleneck.

**Pro tip**: The trading strategy examples in the code include detailed comments about market microstructure, order flow, and risk management techniques.

## Configuration Tips

### Order Book Depth Settings

The most important performance knob is how many price levels to track:

```rust
// For most trading bots: 10-50 levels is plenty
let book_manager = OrderBookManager::new(20);

// For market making: maybe 100+ levels
let book_manager = OrderBookManager::new(100);

// For analysis/research: could go higher, but memory usage grows
let book_manager = OrderBookManager::new(500);
```

Why this matters: Each price level takes memory, but 90% of trading happens in the top 10 levels anyway. More levels = more memory usage for diminishing returns.

*The code comments in `src/book.rs` explain the memory layout and why we chose these specific data structures for different use cases.*

### WebSocket Reconnection

The defaults are pretty good, but you can tune them:

```rust
let reconnect_config = ReconnectConfig {
    max_retries: 5,                                    // Give up after 5 attempts
    base_delay: Duration::from_secs(1),               // Start with 1 second delay
    max_delay: Duration::from_secs(60),               // Cap at 1 minute
    backoff_multiplier: 2.0,                          // Double delay each time
};

let stream = WebSocketStream::new("wss://clob.polymarket.com/ws")
    .with_reconnect_config(reconnect_config);
```

### Memory Usage

If you're tracking lots of tokens, you might want to clean up stale books:

```rust
// Remove books that haven't updated in 5 minutes
let removed = book_manager.cleanup_stale_books(Duration::from_secs(300))?;
println!("Cleaned up {} stale order books", removed);
```

## Error Handling (Because Things Break)

The library tries to be helpful about what went wrong:

```rust
use polyfill_rs::errors::PolyfillError;

match book_manager.apply_delta(delta) {
    Ok(_) => {
        // Order book updated successfully
    }
    Err(PolyfillError::Validation { message, .. }) => {
        // Bad data (price not aligned to tick size, etc.)
        eprintln!("Invalid data: {}", message);
    }
    Err(PolyfillError::Network { .. }) => {
        // Network problems - probably worth retrying
        eprintln!("Network error, will retry...");
    }
    Err(PolyfillError::RateLimit { retry_after, .. }) => {
        // Hit rate limits - back off
        if let Some(delay) = retry_after {
            tokio::time::sleep(delay).await;
        }
    }
    Err(PolyfillError::Stream { kind, .. }) => {
        // WebSocket issues - the library will try to reconnect automatically
        eprintln!("Stream error: {:?}", kind);
    }
    Err(e) => {
        eprintln!("Something else went wrong: {}", e);
    }
}
```

Most errors tell you whether they're worth retrying or if you should give up.

## What's Different From Other Libraries?

### Performance
Most trading libraries are built for "demo day" - they work fine for small examples but fall apart under real load. This one is designed for people who actually need to process thousands of updates per second.

### Tick Alignment
The library enforces price tick alignment automatically. If someone sends you a price that doesn't align to the market's tick size (like $0.6543 when the tick size is $0.01), it gets rejected. This prevents weird pricing bugs.

*The tick alignment code includes detailed comments about why this matters for market integrity and how the integer math makes validation nearly free.*

### Memory Management
Order books can grow huge if you're not careful. The library automatically trims them to keep only the relevant price levels, and you can clean up stale books that haven't updated recently.