mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-08-15 05:28:07 +00:00
fix: resolve rustfmt configuration duplicate key error and apply consistent code formatting across all source files
This commit is contained in:
@@ -6,32 +6,35 @@ use tokio::time::{sleep, Duration};
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 Advanced Network Optimizations - polyfill-rs");
|
||||
println!("===============================================");
|
||||
|
||||
|
||||
// Use the best-performing configuration (Internet)
|
||||
let client = ClobClient::new_internet("https://clob.polymarket.com");
|
||||
|
||||
|
||||
println!("📊 Test 1: Connection Pre-warming");
|
||||
println!("=================================");
|
||||
|
||||
|
||||
// Test without pre-warming
|
||||
let start = Instant::now();
|
||||
let _ = client.get_server_time().await;
|
||||
let cold_start = start.elapsed();
|
||||
println!(" ❄️ Cold start: {:?}", cold_start);
|
||||
|
||||
|
||||
// Test with pre-warming
|
||||
let client_warm = ClobClient::new_internet("https://clob.polymarket.com");
|
||||
let _ = client_warm.prewarm_connections().await;
|
||||
|
||||
|
||||
let start = Instant::now();
|
||||
let _ = client_warm.get_server_time().await;
|
||||
let warm_start = start.elapsed();
|
||||
println!(" 🔥 Warm start: {:?}", warm_start);
|
||||
println!(" 📈 Improvement: {:.1}x faster", cold_start.as_millis() as f64 / warm_start.as_millis() as f64);
|
||||
|
||||
println!(
|
||||
" 📈 Improvement: {:.1}x faster",
|
||||
cold_start.as_millis() as f64 / warm_start.as_millis() as f64
|
||||
);
|
||||
|
||||
println!("\n📊 Test 2: Request Batching Simulation");
|
||||
println!("=====================================");
|
||||
|
||||
|
||||
// Sequential requests
|
||||
let start = Instant::now();
|
||||
for _ in 0..5 {
|
||||
@@ -39,18 +42,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
let sequential_time = start.elapsed();
|
||||
println!(" 📝 Sequential: 5 requests in {:?}", sequential_time);
|
||||
|
||||
|
||||
// Parallel requests (simulating batching)
|
||||
let start = Instant::now();
|
||||
let futures = (0..5).map(|_| client.get_server_time());
|
||||
let _results: Vec<_> = futures_util::future::join_all(futures).await;
|
||||
let parallel_time = start.elapsed();
|
||||
println!(" ⚡ Parallel: 5 requests in {:?}", parallel_time);
|
||||
println!(" 📈 Improvement: {:.1}x faster", sequential_time.as_millis() as f64 / parallel_time.as_millis() as f64);
|
||||
|
||||
println!(
|
||||
" 📈 Improvement: {:.1}x faster",
|
||||
sequential_time.as_millis() as f64 / parallel_time.as_millis() as f64
|
||||
);
|
||||
|
||||
println!("\n📊 Test 3: Circuit Breaker Pattern");
|
||||
println!("=================================");
|
||||
|
||||
|
||||
struct SimpleCircuitBreaker {
|
||||
failure_count: u32,
|
||||
failure_threshold: u32,
|
||||
@@ -58,14 +64,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
last_failure: Option<Instant>,
|
||||
state: CircuitState,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum CircuitState {
|
||||
Closed, // Normal operation
|
||||
Open, // Failing, reject requests
|
||||
HalfOpen, // Testing if service recovered
|
||||
}
|
||||
|
||||
|
||||
impl SimpleCircuitBreaker {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
@@ -76,7 +82,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
state: CircuitState::Closed,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn can_execute(&mut self) -> bool {
|
||||
match self.state {
|
||||
CircuitState::Closed => true,
|
||||
@@ -91,30 +97,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
CircuitState::HalfOpen => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn on_success(&mut self) {
|
||||
self.failure_count = 0;
|
||||
self.state = CircuitState::Closed;
|
||||
}
|
||||
|
||||
|
||||
fn on_failure(&mut self) {
|
||||
self.failure_count += 1;
|
||||
self.last_failure = Some(Instant::now());
|
||||
|
||||
|
||||
if self.failure_count >= self.failure_threshold {
|
||||
self.state = CircuitState::Open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let mut circuit_breaker = SimpleCircuitBreaker::new();
|
||||
let mut successful_requests = 0;
|
||||
let mut rejected_requests = 0;
|
||||
|
||||
|
||||
// Simulate some requests with circuit breaker
|
||||
for i in 0..10 {
|
||||
if circuit_breaker.can_execute() {
|
||||
@@ -125,13 +131,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if i < 3 {
|
||||
println!(" ✅ Request {} succeeded", i + 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
circuit_breaker.on_failure();
|
||||
if i < 3 {
|
||||
println!(" ❌ Request {} failed", i + 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
rejected_requests += 1;
|
||||
@@ -139,21 +145,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" 🚫 Request {} rejected by circuit breaker", i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Small delay between requests
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
println!(" 📊 Results: {} successful, {} rejected", successful_requests, rejected_requests);
|
||||
|
||||
|
||||
println!(
|
||||
" 📊 Results: {} successful, {} rejected",
|
||||
successful_requests, rejected_requests
|
||||
);
|
||||
|
||||
println!("\n📊 Test 4: Adaptive Timeout Strategy");
|
||||
println!("===================================");
|
||||
|
||||
|
||||
struct AdaptiveTimeout {
|
||||
recent_times: Vec<Duration>,
|
||||
max_samples: usize,
|
||||
}
|
||||
|
||||
|
||||
impl AdaptiveTimeout {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
@@ -161,27 +170,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
max_samples: 10,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn add_sample(&mut self, duration: Duration) {
|
||||
self.recent_times.push(duration);
|
||||
if self.recent_times.len() > self.max_samples {
|
||||
self.recent_times.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_adaptive_timeout(&self) -> Duration {
|
||||
if self.recent_times.is_empty() {
|
||||
return Duration::from_millis(5000); // Default
|
||||
}
|
||||
|
||||
|
||||
let avg = self.recent_times.iter().sum::<Duration>() / self.recent_times.len() as u32;
|
||||
// Set timeout to 3x average response time
|
||||
avg * 3
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let mut adaptive_timeout = AdaptiveTimeout::new();
|
||||
|
||||
|
||||
// Collect some samples
|
||||
for i in 0..5 {
|
||||
let start = Instant::now();
|
||||
@@ -193,10 +202,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let recommended_timeout = adaptive_timeout.get_adaptive_timeout();
|
||||
println!(" 🎯 Recommended timeout: {:?}", recommended_timeout);
|
||||
|
||||
|
||||
println!("\n🎯 Advanced Optimization Summary");
|
||||
println!("===============================");
|
||||
println!("Implemented Optimizations:");
|
||||
@@ -204,7 +213,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" ✅ Request parallelization (batching simulation)");
|
||||
println!(" ✅ Circuit breaker pattern (prevents cascade failures)");
|
||||
println!(" ✅ Adaptive timeouts (dynamic based on network conditions)");
|
||||
|
||||
|
||||
println!("\nFurther Optimizations Available:");
|
||||
println!(" 🔧 Custom DNS resolver with caching");
|
||||
println!(" 🔧 Connection affinity (sticky connections)");
|
||||
@@ -212,12 +221,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" 🔧 Geographical load balancing");
|
||||
println!(" 🔧 WebSocket connections for real-time data");
|
||||
println!(" 🔧 HTTP/3 (QUIC) when supported");
|
||||
|
||||
|
||||
println!("\n📈 Expected Network Improvements:");
|
||||
println!(" • 10-30% latency reduction from optimized HTTP client");
|
||||
println!(" • 50-80% improvement in connection reuse scenarios");
|
||||
println!(" • Better resilience during network instability");
|
||||
println!(" • Adaptive performance based on network conditions");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Load environment variables from .env file
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
|
||||
println!("🔐 Authenticated Network Benchmark - Real Order Creation");
|
||||
println!("=======================================================");
|
||||
|
||||
|
||||
// API credentials from .env file
|
||||
let _api_key = std::env::var("POLYMARKET_API_KEY")
|
||||
.map_err(|_| "POLYMARKET_API_KEY not found in .env file")?;
|
||||
@@ -18,17 +18,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.map_err(|_| "POLYMARKET_SECRET not found in .env file")?;
|
||||
let _passphrase = std::env::var("POLYMARKET_PASSPHRASE")
|
||||
.map_err(|_| "POLYMARKET_PASSPHRASE not found in .env file")?;
|
||||
|
||||
|
||||
println!("✅ Loaded API credentials from .env file");
|
||||
|
||||
|
||||
let client = ClobClient::new_internet("https://clob.polymarket.com");
|
||||
|
||||
|
||||
println!("🔑 Setting up API credentials...");
|
||||
|
||||
|
||||
// Test 1: API Key Creation/Derivation (part of the 266.5ms benchmark)
|
||||
println!("\n📊 Test 1: API Key Setup");
|
||||
println!("========================");
|
||||
|
||||
|
||||
let mut setup_times = Vec::new();
|
||||
for i in 0..3 {
|
||||
let start = Instant::now();
|
||||
@@ -36,33 +36,33 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(_creds) => {
|
||||
let duration = start.elapsed();
|
||||
setup_times.push(duration);
|
||||
println!(" Run {}: ✅ API key setup in {:?}", i+1, duration);
|
||||
|
||||
println!(" Run {}: ✅ API key setup in {:?}", i + 1, duration);
|
||||
|
||||
// Set the credentials for order creation
|
||||
// Note: We'd need to properly set up the client with these creds
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
setup_times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
}
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !setup_times.is_empty() {
|
||||
let avg = setup_times.iter().sum::<std::time::Duration>() / setup_times.len() as u32;
|
||||
println!(" 📈 API setup average: {:?}", avg);
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Order Creation with EIP-712 (the real 266.5ms test)
|
||||
println!("\n📊 Test 2: Order Creation + EIP-712 Signing");
|
||||
println!("===========================================");
|
||||
println!("Target: polymarket-rs-client 266.5ms ± 28.6ms");
|
||||
|
||||
|
||||
// We need a real token ID for a valid order
|
||||
let token_id = "21742633143463906290569050155826241533067272736897614950488156847949938836455";
|
||||
|
||||
|
||||
let mut order_times = Vec::new();
|
||||
for i in 0..5 {
|
||||
let order_args = OrderArgs::new(
|
||||
@@ -71,56 +71,70 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Decimal::from_str("1.0").unwrap(), // Minimum size
|
||||
Side::BUY,
|
||||
);
|
||||
|
||||
|
||||
let start = Instant::now();
|
||||
match client.create_order(&order_args, None, None, None).await {
|
||||
Ok(order) => {
|
||||
let duration = start.elapsed();
|
||||
order_times.push(duration);
|
||||
println!(" Run {}: ✅ Order created in {:?}", i+1, duration);
|
||||
|
||||
println!(" Run {}: ✅ Order created in {:?}", i + 1, duration);
|
||||
|
||||
// Immediately cancel to clean up
|
||||
// Note: We'd need the proper cancel method here
|
||||
println!(" 📝 Order ID: {} (would cancel immediately)",
|
||||
format!("{:?}", order).chars().take(50).collect::<String>());
|
||||
}
|
||||
println!(
|
||||
" 📝 Order ID: {} (would cancel immediately)",
|
||||
format!("{:?}", order).chars().take(50).collect::<String>()
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
order_times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
|
||||
// Even errors give us timing info about how far we got
|
||||
if duration.as_millis() > 50 {
|
||||
println!(" 💡 Error occurred after network round-trip, timing still valid");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !order_times.is_empty() {
|
||||
let avg = order_times.iter().sum::<std::time::Duration>() / order_times.len() as u32;
|
||||
let min = order_times.iter().min().unwrap();
|
||||
let max = order_times.iter().max().unwrap();
|
||||
let std_dev = {
|
||||
let mean = avg.as_millis() as f64;
|
||||
let variance = order_times.iter()
|
||||
let variance = order_times
|
||||
.iter()
|
||||
.map(|t| (t.as_millis() as f64 - mean).powi(2))
|
||||
.sum::<f64>() / order_times.len() as f64;
|
||||
.sum::<f64>()
|
||||
/ order_times.len() as f64;
|
||||
variance.sqrt()
|
||||
};
|
||||
|
||||
|
||||
println!("\n 📊 Order Creation Results:");
|
||||
println!(" 📈 polyfill-rs: {:.1}ms ± {:.1}ms", avg.as_millis(), std_dev);
|
||||
println!(
|
||||
" 📈 polyfill-rs: {:.1}ms ± {:.1}ms",
|
||||
avg.as_millis(),
|
||||
std_dev
|
||||
);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🆚 vs original (266.5ms): {:.1}x {}",
|
||||
266.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 267 { "faster" } else { "slower" });
|
||||
println!(
|
||||
" 🆚 vs original (266.5ms): {:.1}x {}",
|
||||
266.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 267 {
|
||||
"faster"
|
||||
} else {
|
||||
"slower"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Compare with Market Data (for context)
|
||||
println!("\n📊 Test 3: Market Data (for comparison)");
|
||||
println!("======================================");
|
||||
|
||||
|
||||
let mut market_times = Vec::new();
|
||||
for i in 0..3 {
|
||||
let start = Instant::now();
|
||||
@@ -129,41 +143,50 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
market_times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
market_times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !market_times.is_empty() {
|
||||
let avg = market_times.iter().sum::<std::time::Duration>() / market_times.len() as u32;
|
||||
println!(" 📈 Market data average: {:?}", avg);
|
||||
println!(" 🆚 vs original (404.5ms): {:.1}x faster", 404.5 / avg.as_millis() as f64);
|
||||
println!(
|
||||
" 🆚 vs original (404.5ms): {:.1}x faster",
|
||||
404.5 / avg.as_millis() as f64
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
println!("\n🎯 Authenticated Benchmark Summary");
|
||||
println!("=================================");
|
||||
println!("Real Production Performance:");
|
||||
|
||||
|
||||
if !order_times.is_empty() {
|
||||
let order_avg = order_times.iter().sum::<std::time::Duration>() / order_times.len() as u32;
|
||||
println!(" • Order creation: {:?} (vs 266.5ms baseline)", order_avg);
|
||||
}
|
||||
|
||||
|
||||
if !market_times.is_empty() {
|
||||
let market_avg = market_times.iter().sum::<std::time::Duration>() / market_times.len() as u32;
|
||||
let market_avg =
|
||||
market_times.iter().sum::<std::time::Duration>() / market_times.len() as u32;
|
||||
println!(" • Market data: {:?} (vs 404.5ms baseline)", market_avg);
|
||||
}
|
||||
|
||||
|
||||
println!("\nThis gives us the REAL production numbers to compare!");
|
||||
println!("Network optimizations + EIP-712 signing performance combined.");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+52
-38
@@ -7,107 +7,116 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 polyfill-rs Performance Benchmark Demo");
|
||||
println!("==========================================");
|
||||
|
||||
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Benchmark 1: Order creation and EIP-712 signing (computational cost)
|
||||
println!("\n📊 Benchmark 1: Order Creation + EIP-712 Signing");
|
||||
println!("------------------------------------------------");
|
||||
|
||||
|
||||
let order_args = OrderArgs::new(
|
||||
"test_token_id",
|
||||
Decimal::from_str("0.75")?,
|
||||
Decimal::from_str("100.0")?,
|
||||
Side::BUY,
|
||||
);
|
||||
|
||||
|
||||
let mut order_times = Vec::new();
|
||||
for i in 0..10 {
|
||||
let start = Instant::now();
|
||||
|
||||
|
||||
// This measures the computational cost of order creation and signing
|
||||
// Note: Will fail without proper credentials, but we're measuring the CPU work
|
||||
let _result = client.create_order(&order_args, None, None, None).await;
|
||||
|
||||
|
||||
let duration = start.elapsed();
|
||||
order_times.push(duration);
|
||||
|
||||
|
||||
if i == 0 {
|
||||
println!(" First run: {:?}", duration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let avg_order_time = order_times.iter().sum::<std::time::Duration>() / order_times.len() as u32;
|
||||
let min_order_time = order_times.iter().min().unwrap();
|
||||
let max_order_time = order_times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" Average: {:?}", avg_order_time);
|
||||
println!(" Range: {:?} - {:?}", min_order_time, max_order_time);
|
||||
println!(" 📈 vs baseline (266.5ms): {:.1}x faster",
|
||||
266.5 / avg_order_time.as_millis() as f64);
|
||||
|
||||
println!(
|
||||
" 📈 vs baseline (266.5ms): {:.1}x faster",
|
||||
266.5 / avg_order_time.as_millis() as f64
|
||||
);
|
||||
|
||||
// Benchmark 2: Market data fetching and parsing
|
||||
println!("\n📊 Benchmark 2: Fetch + Parse Simplified Markets");
|
||||
println!("-----------------------------------------------");
|
||||
|
||||
|
||||
let mut fetch_times = Vec::new();
|
||||
for i in 0..5 {
|
||||
let start = Instant::now();
|
||||
|
||||
|
||||
match client.get_sampling_simplified_markets(None).await {
|
||||
Ok(markets) => {
|
||||
let duration = start.elapsed();
|
||||
fetch_times.push(duration);
|
||||
|
||||
|
||||
if i == 0 {
|
||||
println!(" ✅ Fetched {} markets in {:?}", markets.data.len(), duration);
|
||||
println!(
|
||||
" ✅ Fetched {} markets in {:?}",
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
println!(" ⚠️ Network error (expected): {} in {:?}", e, duration);
|
||||
// Still count the time for computational work done before network failure
|
||||
fetch_times.push(duration);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !fetch_times.is_empty() {
|
||||
let avg_fetch_time = fetch_times.iter().sum::<std::time::Duration>() / fetch_times.len() as u32;
|
||||
let avg_fetch_time =
|
||||
fetch_times.iter().sum::<std::time::Duration>() / fetch_times.len() as u32;
|
||||
let min_fetch_time = fetch_times.iter().min().unwrap();
|
||||
let max_fetch_time = fetch_times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" Average: {:?}", avg_fetch_time);
|
||||
println!(" Range: {:?} - {:?}", min_fetch_time, max_fetch_time);
|
||||
println!(" 📈 vs baseline (404.5ms): {:.1}x faster",
|
||||
404.5 / avg_fetch_time.as_millis() as f64);
|
||||
println!(
|
||||
" 📈 vs baseline (404.5ms): {:.1}x faster",
|
||||
404.5 / avg_fetch_time.as_millis() as f64
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Benchmark 3: Memory efficiency demonstration
|
||||
println!("\n📊 Benchmark 3: Memory Usage Analysis");
|
||||
println!("------------------------------------");
|
||||
|
||||
|
||||
println!(" 🔧 Memory optimizations in polyfill-rs:");
|
||||
println!(" • Fixed-point arithmetic (u32/i64 vs Decimal)");
|
||||
println!(" • Zero-allocation order book updates");
|
||||
println!(" • Compact data structures");
|
||||
println!(" • Cache-aligned memory layouts");
|
||||
println!(" 📈 Expected: ~10x less memory vs baseline (15.9MB)");
|
||||
|
||||
|
||||
// Demonstrate order book efficiency
|
||||
println!("\n📊 Benchmark 4: Order Book Performance");
|
||||
println!("------------------------------------");
|
||||
|
||||
|
||||
use polyfill_rs::OrderBookImpl;
|
||||
|
||||
|
||||
let mut book = OrderBookImpl::new("demo_token".to_string(), 100);
|
||||
let start = Instant::now();
|
||||
|
||||
|
||||
// Simulate rapid order book updates
|
||||
for i in 0..10000 {
|
||||
let price = Decimal::from_str(&format!("0.{:04}", 5000 + (i % 1000)))?;
|
||||
let size = Decimal::from_str("100.0")?;
|
||||
|
||||
|
||||
// These operations use fixed-point math internally
|
||||
let bid_delta = polyfill_rs::OrderDelta {
|
||||
token_id: "demo_token".to_string(),
|
||||
@@ -125,16 +134,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
size,
|
||||
sequence: (i + 10000) as u64,
|
||||
};
|
||||
|
||||
|
||||
let _ = book.apply_delta(bid_delta);
|
||||
let _ = book.apply_delta(ask_delta);
|
||||
}
|
||||
|
||||
|
||||
let book_duration = start.elapsed();
|
||||
println!(" ⚡ 20,000 order book updates in {:?}", book_duration);
|
||||
println!(" 📊 Rate: {:.0} updates/second",
|
||||
20000.0 / book_duration.as_secs_f64());
|
||||
|
||||
println!(
|
||||
" 📊 Rate: {:.0} updates/second",
|
||||
20000.0 / book_duration.as_secs_f64()
|
||||
);
|
||||
|
||||
// Fast operations
|
||||
let start = Instant::now();
|
||||
for _ in 0..100000 {
|
||||
@@ -142,8 +153,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let _ = book.mid_price_fast();
|
||||
}
|
||||
let fast_ops_duration = start.elapsed();
|
||||
println!(" ⚡ 200,000 fast spread/mid calculations in {:?}", fast_ops_duration);
|
||||
|
||||
println!(
|
||||
" ⚡ 200,000 fast spread/mid calculations in {:?}",
|
||||
fast_ops_duration
|
||||
);
|
||||
|
||||
println!("\n🎯 Summary");
|
||||
println!("=========");
|
||||
println!("polyfill-rs delivers significant performance improvements through:");
|
||||
@@ -154,6 +168,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!();
|
||||
println!("🔬 Run `cargo bench` for detailed criterion benchmarks");
|
||||
println!("📊 Run `./scripts/benchmark_comparison.sh` for comprehensive analysis");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+199
-164
@@ -10,36 +10,40 @@
|
||||
//! - Rate limiting and performance optimizations
|
||||
|
||||
use polyfill_rs::{
|
||||
// Core client types
|
||||
ClobClient, PolyfillClient, OrderArgs, Side, OrderType,
|
||||
|
||||
// Order book management
|
||||
book::{OrderBook, OrderBookManager},
|
||||
|
||||
// Streaming capabilities
|
||||
stream::{WebSocketStream, StreamManager},
|
||||
|
||||
// Fill execution
|
||||
fill::{FillEngine, FillProcessor},
|
||||
|
||||
// Types and structures
|
||||
types::*,
|
||||
|
||||
|
||||
// Error handling
|
||||
errors::{PolyfillError, Result},
|
||||
|
||||
|
||||
// Fill execution
|
||||
fill::{FillEngine, FillProcessor},
|
||||
|
||||
// Streaming capabilities
|
||||
stream::{StreamManager, WebSocketStream},
|
||||
|
||||
// Types and structures
|
||||
types::*,
|
||||
|
||||
// Utility functions
|
||||
utils::{crypto, math, retry, time, url, rate_limit, address},
|
||||
|
||||
utils::{address, crypto, math, rate_limit, retry, time, url},
|
||||
|
||||
// Configuration
|
||||
ClientConfig,
|
||||
// Core client types
|
||||
ClobClient,
|
||||
OrderArgs,
|
||||
OrderType,
|
||||
|
||||
PolyfillClient,
|
||||
Side,
|
||||
};
|
||||
|
||||
use rust_decimal::Decimal;
|
||||
use rust_decimal_macros::dec;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{error, info, debug};
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
/// Comprehensive demo showcasing all polyfill-rs functionality
|
||||
#[allow(dead_code)]
|
||||
@@ -91,40 +95,40 @@ impl PolyfillDemo {
|
||||
pub fn new() -> Result<Self> {
|
||||
// Create basic client
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Create advanced client with configuration
|
||||
let _config = ClientConfig {
|
||||
base_url: "https://clob.polymarket.com".to_string(),
|
||||
chain_id: 137, // Polygon
|
||||
private_key: None, // Would be set in production
|
||||
api_credentials: None, // Would be set in production
|
||||
chain_id: 137, // Polygon
|
||||
private_key: None, // Would be set in production
|
||||
api_credentials: None, // Would be set in production
|
||||
max_slippage: Some(dec!(0.01)), // 1% max slippage
|
||||
fee_rate: Some(dec!(0.02)), // 2% fee rate
|
||||
fee_rate: Some(dec!(0.02)), // 2% fee rate
|
||||
timeout: Some(Duration::from_secs(30)),
|
||||
max_connections: Some(100),
|
||||
};
|
||||
|
||||
|
||||
let advanced_client = PolyfillClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Create order book manager
|
||||
let book_manager = OrderBookManager::new(100);
|
||||
|
||||
|
||||
// Create fill engine
|
||||
let fill_engine = FillEngine::new(
|
||||
dec!(1.0), // Min fill size
|
||||
dec!(2.0), // Max slippage 2%
|
||||
5, // 5 bps fee rate
|
||||
dec!(1.0), // Min fill size
|
||||
dec!(2.0), // Max slippage 2%
|
||||
5, // 5 bps fee rate
|
||||
);
|
||||
|
||||
|
||||
// Create fill processor
|
||||
let fill_processor = FillProcessor::new(1000);
|
||||
|
||||
|
||||
// Create stream manager
|
||||
let stream_manager = StreamManager::new();
|
||||
|
||||
|
||||
// Create rate limiter (100 requests per second)
|
||||
let rate_limiter = rate_limit::TokenBucket::new(100, 100);
|
||||
|
||||
|
||||
Ok(Self {
|
||||
client,
|
||||
advanced_client,
|
||||
@@ -140,24 +144,24 @@ impl PolyfillDemo {
|
||||
/// Demo 1: Basic API Operations
|
||||
pub async fn demo_basic_api_operations(&mut self) -> Result<()> {
|
||||
info!("=== Demo 1: Basic API Operations ===");
|
||||
|
||||
|
||||
// Test connectivity
|
||||
let is_ok = self.client.get_ok().await;
|
||||
info!("API connectivity: {}", is_ok);
|
||||
self.stats.api_calls += 1;
|
||||
|
||||
|
||||
// Get server time
|
||||
match self.client.get_server_time().await {
|
||||
Ok(timestamp) => {
|
||||
info!("Server time: {}", timestamp);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get server time: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Get sampling markets
|
||||
match self.client.get_sampling_markets(None).await {
|
||||
Ok(markets) => {
|
||||
@@ -166,32 +170,36 @@ impl PolyfillDemo {
|
||||
info!(" Market: {} - {}", market.question, market.market_slug);
|
||||
}
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get markets: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 2: Order Book Operations
|
||||
pub async fn demo_order_book_operations(&mut self) -> Result<()> {
|
||||
info!("=== Demo 2: Order Book Operations ===");
|
||||
|
||||
|
||||
// Example token ID (you would use a real one in production)
|
||||
let token_id = "12345";
|
||||
|
||||
|
||||
// Get order book from API
|
||||
match self.client.get_order_book(token_id).await {
|
||||
Ok(order_book) => {
|
||||
info!("Order book for token {}: {} bids, {} asks",
|
||||
token_id, order_book.bids.len(), order_book.asks.len());
|
||||
|
||||
info!(
|
||||
"Order book for token {}: {} bids, {} asks",
|
||||
token_id,
|
||||
order_book.bids.len(),
|
||||
order_book.asks.len()
|
||||
);
|
||||
|
||||
// Create local order book
|
||||
let mut local_book = OrderBook::new(token_id.to_string(), 50);
|
||||
|
||||
|
||||
// Apply order book data to local book
|
||||
for (i, bid) in order_book.bids.iter().enumerate() {
|
||||
local_book.apply_delta(OrderDelta {
|
||||
@@ -203,7 +211,7 @@ impl PolyfillDemo {
|
||||
sequence: i as u64,
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
for (i, ask) in order_book.asks.iter().enumerate() {
|
||||
local_book.apply_delta(OrderDelta {
|
||||
token_id: token_id.to_string(),
|
||||
@@ -214,19 +222,29 @@ impl PolyfillDemo {
|
||||
sequence: (order_book.bids.len() + i) as u64,
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
// Get analytics
|
||||
let analytics = local_book.analytics();
|
||||
info!("Book analytics:");
|
||||
info!(" Bid levels: {}, Ask levels: {}", analytics.bid_count, analytics.ask_count);
|
||||
info!(" Total bid size: {}, Total ask size: {}", analytics.total_bid_size, analytics.total_ask_size);
|
||||
info!(
|
||||
" Bid levels: {}, Ask levels: {}",
|
||||
analytics.bid_count, analytics.ask_count
|
||||
);
|
||||
info!(
|
||||
" Total bid size: {}, Total ask size: {}",
|
||||
analytics.total_bid_size, analytics.total_ask_size
|
||||
);
|
||||
if let Some(spread) = analytics.spread {
|
||||
info!(" Spread: {} ({:.2}%)", spread, analytics.spread_pct.unwrap_or(dec!(0)));
|
||||
info!(
|
||||
" Spread: {} ({:.2}%)",
|
||||
spread,
|
||||
analytics.spread_pct.unwrap_or(dec!(0))
|
||||
);
|
||||
}
|
||||
if let Some(mid) = analytics.mid_price {
|
||||
info!(" Mid price: {}", mid);
|
||||
}
|
||||
|
||||
|
||||
// Calculate market impact
|
||||
if let Some(impact) = local_book.calculate_market_impact(Side::BUY, dec!(100.0)) {
|
||||
info!("Market impact for 100 size buy:");
|
||||
@@ -234,103 +252,98 @@ impl PolyfillDemo {
|
||||
info!(" Impact: {:.2}%", impact.impact_pct);
|
||||
info!(" Total cost: {}", impact.total_cost);
|
||||
}
|
||||
|
||||
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get order book: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 3: Market Data Operations
|
||||
pub async fn demo_market_data_operations(&mut self) -> Result<()> {
|
||||
info!("=== Demo 3: Market Data Operations ===");
|
||||
|
||||
|
||||
let token_id = "12345";
|
||||
|
||||
|
||||
// Get midpoint
|
||||
match self.client.get_midpoint(token_id).await {
|
||||
Ok(midpoint) => {
|
||||
info!("Midpoint for {}: {}", token_id, midpoint.mid);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get midpoint: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Get spread
|
||||
match self.client.get_spread(token_id).await {
|
||||
Ok(spread) => {
|
||||
info!("Spread for {}: {}", token_id, spread.spread);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get spread: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Get price for both sides
|
||||
for side in [Side::BUY, Side::SELL] {
|
||||
match self.client.get_price(token_id, side).await {
|
||||
Ok(price) => {
|
||||
info!("{} price for {}: {}", side.as_str(), token_id, price.price);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get {} price: {}", side.as_str(), e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get tick size
|
||||
match self.client.get_tick_size(token_id).await {
|
||||
Ok(tick_size) => {
|
||||
info!("Tick size for {}: {}", token_id, tick_size);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get tick size: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Get neg risk
|
||||
match self.client.get_neg_risk(token_id).await {
|
||||
Ok(neg_risk) => {
|
||||
info!("Neg risk for {}: {}", token_id, neg_risk);
|
||||
self.stats.api_calls += 1;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get neg risk: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 4: Order Creation and Management
|
||||
pub async fn demo_order_operations(&mut self) -> Result<()> {
|
||||
info!("=== Demo 4: Order Creation and Management ===");
|
||||
|
||||
|
||||
// Create order arguments
|
||||
let order_args = OrderArgs::new(
|
||||
"12345",
|
||||
dec!(0.75),
|
||||
dec!(100.0),
|
||||
Side::BUY,
|
||||
);
|
||||
|
||||
let order_args = OrderArgs::new("12345", dec!(0.75), dec!(100.0), Side::BUY);
|
||||
|
||||
info!("Created order args: {:?}", order_args);
|
||||
|
||||
|
||||
// Create market order request
|
||||
let market_order = MarketOrderRequest {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -339,9 +352,9 @@ impl PolyfillDemo {
|
||||
slippage_tolerance: Some(dec!(1.0)), // 1% slippage
|
||||
client_id: Some("demo_market_order".to_string()),
|
||||
};
|
||||
|
||||
|
||||
info!("Created market order request: {:?}", market_order);
|
||||
|
||||
|
||||
// Create limit order request
|
||||
let limit_order = OrderRequest {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -352,21 +365,21 @@ impl PolyfillDemo {
|
||||
expiration: None,
|
||||
client_id: Some("demo_limit_order".to_string()),
|
||||
};
|
||||
|
||||
|
||||
info!("Created limit order request: {:?}", limit_order);
|
||||
|
||||
|
||||
self.stats.orders_processed += 2;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 5: Fill Execution
|
||||
pub async fn demo_fill_execution(&mut self) -> Result<()> {
|
||||
info!("=== Demo 5: Fill Execution ===");
|
||||
|
||||
|
||||
// Create a mock order book for testing
|
||||
let mut book = OrderBook::new("12345".to_string(), 50);
|
||||
|
||||
|
||||
// Add some liquidity
|
||||
for i in 1..=5 {
|
||||
book.apply_delta(OrderDelta {
|
||||
@@ -378,7 +391,7 @@ impl PolyfillDemo {
|
||||
sequence: i,
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
for i in 1..=5 {
|
||||
book.apply_delta(OrderDelta {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -389,9 +402,9 @@ impl PolyfillDemo {
|
||||
sequence: i + 10,
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
info!("Created order book with liquidity");
|
||||
|
||||
|
||||
// Execute market order
|
||||
let market_order = MarketOrderRequest {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -400,9 +413,11 @@ impl PolyfillDemo {
|
||||
slippage_tolerance: Some(dec!(2.0)),
|
||||
client_id: Some("demo_market_buy".to_string()),
|
||||
};
|
||||
|
||||
let fill_result = self.fill_engine.execute_market_order(&market_order, &book)?;
|
||||
|
||||
|
||||
let fill_result = self
|
||||
.fill_engine
|
||||
.execute_market_order(&market_order, &book)?;
|
||||
|
||||
info!("Market order execution result:");
|
||||
info!(" Status: {:?}", fill_result.status);
|
||||
info!(" Total size: {}", fill_result.total_size);
|
||||
@@ -410,14 +425,14 @@ impl PolyfillDemo {
|
||||
info!(" Total cost: {}", fill_result.total_cost);
|
||||
info!(" Fees: {}", fill_result.fees);
|
||||
info!(" Number of fills: {}", fill_result.fills.len());
|
||||
|
||||
|
||||
// Process fills
|
||||
for fill in &fill_result.fills {
|
||||
self.fill_processor.process_fill(fill.clone())?;
|
||||
self.stats.fills_processed += 1;
|
||||
self.stats.total_volume += fill.size;
|
||||
}
|
||||
|
||||
|
||||
// Execute limit order
|
||||
let limit_order = OrderRequest {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -428,45 +443,48 @@ impl PolyfillDemo {
|
||||
expiration: None,
|
||||
client_id: Some("demo_limit_sell".to_string()),
|
||||
};
|
||||
|
||||
|
||||
let limit_result = self.fill_engine.execute_limit_order(&limit_order, &book)?;
|
||||
|
||||
|
||||
info!("Limit order execution result:");
|
||||
info!(" Status: {:?}", limit_result.status);
|
||||
info!(" Total size: {}", limit_result.total_size);
|
||||
info!(" Average price: {}", limit_result.average_price);
|
||||
|
||||
|
||||
self.stats.orders_processed += 2;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 6: Utility Functions
|
||||
pub async fn demo_utility_functions(&mut self) -> Result<()> {
|
||||
info!("=== Demo 6: Utility Functions ===");
|
||||
|
||||
|
||||
// Time utilities
|
||||
info!("Time utilities:");
|
||||
info!(" Current timestamp (secs): {}", time::now_secs());
|
||||
info!(" Current timestamp (millis): {}", time::now_millis());
|
||||
info!(" Current timestamp (micros): {}", time::now_micros());
|
||||
|
||||
|
||||
// Math utilities
|
||||
info!("Math utilities:");
|
||||
let price = dec!(0.7534);
|
||||
let tick_size = dec!(0.01);
|
||||
let rounded_price = math::round_to_tick(price, tick_size);
|
||||
info!(" Price: {}, Tick size: {}, Rounded: {}", price, tick_size, rounded_price);
|
||||
|
||||
info!(
|
||||
" Price: {}, Tick size: {}, Rounded: {}",
|
||||
price, tick_size, rounded_price
|
||||
);
|
||||
|
||||
let notional = math::notional(price, dec!(100.0));
|
||||
info!(" Notional value: {}", notional);
|
||||
|
||||
|
||||
let spread_pct = math::spread_pct(dec!(0.75), dec!(0.76));
|
||||
info!(" Spread percentage: {:?}", spread_pct);
|
||||
|
||||
|
||||
let mid_price = math::mid_price(dec!(0.75), dec!(0.76));
|
||||
info!(" Mid price: {:?}", mid_price);
|
||||
|
||||
|
||||
// Address utilities
|
||||
info!("Address utilities:");
|
||||
let address = "0x1234567890123456789012345678901234567890";
|
||||
@@ -474,32 +492,36 @@ impl PolyfillDemo {
|
||||
Ok(addr) => info!(" Parsed address: {:?}", addr),
|
||||
Err(e) => error!(" Failed to parse address: {}", e),
|
||||
}
|
||||
|
||||
|
||||
let token_id = "12345";
|
||||
match address::validate_token_id(token_id) {
|
||||
Ok(_) => info!(" Valid token ID: {}", token_id),
|
||||
Err(e) => error!(" Invalid token ID: {}", e),
|
||||
}
|
||||
|
||||
|
||||
// URL utilities
|
||||
info!("URL utilities:");
|
||||
let endpoint = url::build_endpoint("https://api.example.com", "/v1/orders")?;
|
||||
info!(" Built endpoint: {}", endpoint);
|
||||
|
||||
|
||||
// Rate limiting
|
||||
info!("Rate limiting:");
|
||||
for i in 0..5 {
|
||||
let allowed = self.rate_limiter.try_consume();
|
||||
info!(" Request {}: {}", i + 1, if allowed { "ALLOWED" } else { "RATE LIMITED" });
|
||||
info!(
|
||||
" Request {}: {}",
|
||||
i + 1,
|
||||
if allowed { "ALLOWED" } else { "RATE LIMITED" }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 7: Error Handling and Retry Logic
|
||||
pub async fn demo_error_handling(&mut self) -> Result<()> {
|
||||
info!("=== Demo 7: Error Handling and Retry Logic ===");
|
||||
|
||||
|
||||
// Demonstrate retry logic
|
||||
let retry_config = retry::RetryConfig {
|
||||
max_attempts: 3,
|
||||
@@ -508,53 +530,59 @@ impl PolyfillDemo {
|
||||
backoff_factor: 2.0,
|
||||
jitter: true,
|
||||
};
|
||||
|
||||
|
||||
let operation = || async {
|
||||
// Simulate a potentially failing operation
|
||||
if rand::random::<bool>() {
|
||||
Ok("Success!")
|
||||
} else {
|
||||
Err(PolyfillError::network("Simulated network error", std::io::Error::other("Simulated error")))
|
||||
Err(PolyfillError::network(
|
||||
"Simulated network error",
|
||||
std::io::Error::other("Simulated error"),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match retry::with_retry(&retry_config, operation).await {
|
||||
Ok(result) => {
|
||||
info!("Retry operation succeeded: {}", result);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Retry operation failed after all attempts: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Demonstrate error types
|
||||
info!("Error types demonstration:");
|
||||
|
||||
|
||||
let api_error = PolyfillError::api(400, "Bad Request");
|
||||
info!(" API Error: {:?}", api_error);
|
||||
|
||||
let network_error = PolyfillError::network("Connection timeout", std::io::Error::new(std::io::ErrorKind::TimedOut, "Connection timeout"));
|
||||
|
||||
let network_error = PolyfillError::network(
|
||||
"Connection timeout",
|
||||
std::io::Error::new(std::io::ErrorKind::TimedOut, "Connection timeout"),
|
||||
);
|
||||
info!(" Network Error: {:?}", network_error);
|
||||
|
||||
|
||||
let parse_error = PolyfillError::parse("Invalid JSON", None);
|
||||
info!(" Parse Error: {:?}", parse_error);
|
||||
|
||||
|
||||
let config_error = PolyfillError::config("Invalid configuration");
|
||||
info!(" Config Error: {:?}", config_error);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 8: Streaming Capabilities (Mock)
|
||||
pub async fn demo_streaming_capabilities(&mut self) -> Result<()> {
|
||||
info!("=== Demo 8: Streaming Capabilities ===");
|
||||
|
||||
|
||||
// Create a mock WebSocket stream
|
||||
let _stream = WebSocketStream::new("wss://stream.polymarket.com");
|
||||
|
||||
|
||||
info!("Created WebSocket stream");
|
||||
|
||||
|
||||
// Simulate subscription
|
||||
let subscription = WssSubscription {
|
||||
auth: WssAuth {
|
||||
@@ -567,12 +595,14 @@ impl PolyfillDemo {
|
||||
asset_ids: Some(vec!["12345".to_string(), "67890".to_string()]),
|
||||
channel_type: "USER".to_string(),
|
||||
};
|
||||
|
||||
|
||||
info!("Created subscription: {:?}", subscription);
|
||||
|
||||
|
||||
// Simulate receiving stream messages
|
||||
let messages = vec![
|
||||
StreamMessage::Heartbeat { timestamp: chrono::Utc::now() },
|
||||
StreamMessage::Heartbeat {
|
||||
timestamp: chrono::Utc::now(),
|
||||
},
|
||||
StreamMessage::BookUpdate {
|
||||
data: OrderDelta {
|
||||
token_id: "12345".to_string(),
|
||||
@@ -581,7 +611,7 @@ impl PolyfillDemo {
|
||||
price: dec!(0.75),
|
||||
size: dec!(100.0),
|
||||
sequence: 1,
|
||||
}
|
||||
},
|
||||
},
|
||||
StreamMessage::Trade {
|
||||
data: FillEvent {
|
||||
@@ -595,14 +625,14 @@ impl PolyfillDemo {
|
||||
maker_address: alloy_primitives::Address::ZERO,
|
||||
taker_address: alloy_primitives::Address::ZERO,
|
||||
fee: dec!(0.375),
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
for message in messages {
|
||||
info!("Received stream message: {:?}", message);
|
||||
self.stats.stream_messages += 1;
|
||||
|
||||
|
||||
// Process message based on type
|
||||
match &message {
|
||||
StreamMessage::BookUpdate { data } => {
|
||||
@@ -611,31 +641,35 @@ impl PolyfillDemo {
|
||||
error!(" Failed to apply book update: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
StreamMessage::Trade { data } => {
|
||||
info!(" Processing trade: {} {} @ {}",
|
||||
data.side.as_str(), data.size, data.price);
|
||||
info!(
|
||||
" Processing trade: {} {} @ {}",
|
||||
data.side.as_str(),
|
||||
data.size,
|
||||
data.price
|
||||
);
|
||||
if let Err(e) = self.fill_processor.process_fill(data.clone()) {
|
||||
error!(" Failed to process fill: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
StreamMessage::Heartbeat { timestamp } => {
|
||||
debug!(" Received heartbeat at: {}", timestamp);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
info!(" Unhandled message type");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Demo 9: Performance and Analytics
|
||||
pub async fn demo_performance_analytics(&mut self) -> Result<()> {
|
||||
info!("=== Demo 9: Performance and Analytics ===");
|
||||
|
||||
|
||||
// Get fill engine statistics
|
||||
let fill_stats = self.fill_engine.get_stats();
|
||||
info!("Fill engine statistics:");
|
||||
@@ -643,7 +677,7 @@ impl PolyfillDemo {
|
||||
info!(" Total fills: {}", fill_stats.total_fills);
|
||||
info!(" Total volume: {}", fill_stats.total_volume);
|
||||
info!(" Total fees: {}", fill_stats.total_fees);
|
||||
|
||||
|
||||
// Get fill processor statistics
|
||||
let processor_stats = self.fill_processor.get_stats();
|
||||
info!("Fill processor statistics:");
|
||||
@@ -652,7 +686,7 @@ impl PolyfillDemo {
|
||||
info!(" Pending volume: {}", processor_stats.pending_volume);
|
||||
info!(" Processed fills: {}", processor_stats.processed_fills);
|
||||
info!(" Processed volume: {}", processor_stats.processed_volume);
|
||||
|
||||
|
||||
// Get demo statistics
|
||||
info!("Demo statistics:");
|
||||
info!(" API calls: {}", self.stats.api_calls);
|
||||
@@ -661,50 +695,51 @@ impl PolyfillDemo {
|
||||
info!(" Stream messages: {}", self.stats.stream_messages);
|
||||
info!(" Errors: {}", self.stats.errors);
|
||||
info!(" Total volume: {}", self.stats.total_volume);
|
||||
|
||||
|
||||
// Calculate error rate
|
||||
let total_operations = self.stats.api_calls + self.stats.orders_processed + self.stats.stream_messages;
|
||||
let total_operations =
|
||||
self.stats.api_calls + self.stats.orders_processed + self.stats.stream_messages;
|
||||
let error_rate = if total_operations > 0 {
|
||||
(self.stats.errors as f64 / total_operations as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
info!(" Error rate: {:.2}%", error_rate);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run all demos
|
||||
pub async fn run_all_demos(&mut self) -> Result<()> {
|
||||
info!("Starting comprehensive polyfill-rs demo...");
|
||||
|
||||
|
||||
// Run all demo sections
|
||||
self.demo_basic_api_operations().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_order_book_operations().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_market_data_operations().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_order_operations().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_fill_execution().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_utility_functions().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_error_handling().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_streaming_capabilities().await?;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
|
||||
|
||||
self.demo_performance_analytics().await?;
|
||||
|
||||
|
||||
info!("Comprehensive demo completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
@@ -714,18 +749,18 @@ impl PolyfillDemo {
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize logging
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
|
||||
info!("Polyfill-rs Comprehensive Demo");
|
||||
info!("==============================");
|
||||
|
||||
|
||||
// Create and run demo
|
||||
let mut demo = PolyfillDemo::new()?;
|
||||
|
||||
|
||||
if let Err(e) = demo.run_all_demos().await {
|
||||
error!("Demo failed: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
|
||||
info!("Demo completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,100 +54,125 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Comparing with polymarket-rs-client baseline:");
|
||||
println!(" 88,053 allocs, 81,823 frees, 15,945,966 bytes allocated");
|
||||
println!();
|
||||
|
||||
|
||||
// Load environment variables
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
|
||||
let client = ClobClient::new_internet("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Test 1: Market Data Fetching Memory Usage
|
||||
println!("📊 Test 1: Market Data Fetching Memory");
|
||||
println!("=====================================");
|
||||
|
||||
|
||||
// Reset and measure market data fetching
|
||||
reset_counters();
|
||||
let start_stats = get_memory_stats();
|
||||
|
||||
|
||||
let start_time = Instant::now();
|
||||
let result = client.get_sampling_simplified_markets(None).await;
|
||||
let duration = start_time.elapsed();
|
||||
|
||||
|
||||
let end_stats = get_memory_stats();
|
||||
|
||||
|
||||
match result {
|
||||
Ok(markets) => {
|
||||
println!("✅ Fetched {} markets in {:?}", markets.data.len(), duration);
|
||||
|
||||
println!(
|
||||
"✅ Fetched {} markets in {:?}",
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
|
||||
let (bytes_allocated, allocs, deallocs) = (
|
||||
end_stats.0 - start_stats.0,
|
||||
end_stats.1 - start_stats.1,
|
||||
end_stats.2 - start_stats.2,
|
||||
);
|
||||
|
||||
|
||||
println!("📈 polyfill-rs memory usage:");
|
||||
println!(" {} allocs, {} frees, {} bytes allocated", allocs, deallocs, bytes_allocated);
|
||||
println!("📊 vs baseline (15,945,966 bytes): {:.1}x less memory",
|
||||
15_945_966.0 / bytes_allocated as f64);
|
||||
println!("📊 vs baseline ({} allocs): {:.1}x fewer allocations",
|
||||
88_053, 88_053.0 / allocs as f64);
|
||||
}
|
||||
println!(
|
||||
" {} allocs, {} frees, {} bytes allocated",
|
||||
allocs, deallocs, bytes_allocated
|
||||
);
|
||||
println!(
|
||||
"📊 vs baseline (15,945,966 bytes): {:.1}x less memory",
|
||||
15_945_966.0 / bytes_allocated as f64
|
||||
);
|
||||
println!(
|
||||
"📊 vs baseline ({} allocs): {:.1}x fewer allocations",
|
||||
88_053,
|
||||
88_053.0 / allocs as f64
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("❌ Error: {}", e);
|
||||
println!("⚠️ Still measuring memory usage of error handling...");
|
||||
|
||||
|
||||
let (bytes_allocated, allocs, deallocs) = (
|
||||
end_stats.0 - start_stats.0,
|
||||
end_stats.1 - start_stats.1,
|
||||
end_stats.2 - start_stats.2,
|
||||
);
|
||||
|
||||
|
||||
println!("📈 Memory usage (even with error):");
|
||||
println!(" {} allocs, {} frees, {} bytes allocated", allocs, deallocs, bytes_allocated);
|
||||
}
|
||||
println!(
|
||||
" {} allocs, {} frees, {} bytes allocated",
|
||||
allocs, deallocs, bytes_allocated
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Order Book Memory Efficiency
|
||||
println!("\n📊 Test 2: Order Book Memory Efficiency");
|
||||
println!("======================================");
|
||||
|
||||
|
||||
reset_counters();
|
||||
let start_stats = get_memory_stats();
|
||||
|
||||
|
||||
// Create order book and populate it
|
||||
let mut book = OrderBookImpl::new("test_token".to_string(), 100);
|
||||
|
||||
|
||||
// Add many orders to test memory efficiency
|
||||
for i in 0..1000 {
|
||||
let price = Decimal::from_str(&format!("0.{:04}", 5000 + (i % 100))).unwrap();
|
||||
let size = Decimal::from_str("100.0").unwrap();
|
||||
|
||||
|
||||
let delta = polyfill_rs::OrderDelta {
|
||||
token_id: "test_token".to_string(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: if i % 2 == 0 { polyfill_rs::Side::BUY } else { polyfill_rs::Side::SELL },
|
||||
side: if i % 2 == 0 {
|
||||
polyfill_rs::Side::BUY
|
||||
} else {
|
||||
polyfill_rs::Side::SELL
|
||||
},
|
||||
price,
|
||||
size,
|
||||
sequence: i as u64,
|
||||
};
|
||||
|
||||
|
||||
let _ = book.apply_delta(delta);
|
||||
}
|
||||
|
||||
|
||||
let end_stats = get_memory_stats();
|
||||
let (bytes_allocated, allocs, deallocs) = (
|
||||
end_stats.0 - start_stats.0,
|
||||
end_stats.1 - start_stats.1,
|
||||
end_stats.2 - start_stats.2,
|
||||
);
|
||||
|
||||
|
||||
println!("📈 Order book (1000 updates):");
|
||||
println!(" {} allocs, {} frees, {} bytes allocated", allocs, deallocs, bytes_allocated);
|
||||
println!("📊 Per update: {:.1} bytes/update", bytes_allocated as f64 / 1000.0);
|
||||
|
||||
println!(
|
||||
" {} allocs, {} frees, {} bytes allocated",
|
||||
allocs, deallocs, bytes_allocated
|
||||
);
|
||||
println!(
|
||||
"📊 Per update: {:.1} bytes/update",
|
||||
bytes_allocated as f64 / 1000.0
|
||||
);
|
||||
|
||||
// Test 3: JSON Parsing Memory
|
||||
println!("\n📊 Test 3: JSON Parsing Memory Usage");
|
||||
println!("===================================");
|
||||
|
||||
|
||||
let sample_json = r#"{
|
||||
"data": [
|
||||
{
|
||||
@@ -168,69 +193,83 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
|
||||
reset_counters();
|
||||
let start_stats = get_memory_stats();
|
||||
|
||||
|
||||
// Parse JSON 1000 times to measure memory usage
|
||||
for _ in 0..1000 {
|
||||
let _: Result<serde_json::Value, _> = serde_json::from_str(sample_json);
|
||||
}
|
||||
|
||||
|
||||
let end_stats = get_memory_stats();
|
||||
let (bytes_allocated, allocs, deallocs) = (
|
||||
end_stats.0 - start_stats.0,
|
||||
end_stats.1 - start_stats.1,
|
||||
end_stats.2 - start_stats.2,
|
||||
);
|
||||
|
||||
|
||||
println!("📈 JSON parsing (1000 operations):");
|
||||
println!(" {} allocs, {} frees, {} bytes allocated", allocs, deallocs, bytes_allocated);
|
||||
println!("📊 Per parse: {:.1} bytes/parse", bytes_allocated as f64 / 1000.0);
|
||||
|
||||
println!(
|
||||
" {} allocs, {} frees, {} bytes allocated",
|
||||
allocs, deallocs, bytes_allocated
|
||||
);
|
||||
println!(
|
||||
"📊 Per parse: {:.1} bytes/parse",
|
||||
bytes_allocated as f64 / 1000.0
|
||||
);
|
||||
|
||||
// Test 4: Fixed-point vs Decimal Memory
|
||||
println!("\n📊 Test 4: Fixed-point vs Decimal Memory");
|
||||
println!("=======================================");
|
||||
|
||||
|
||||
// Test Decimal operations
|
||||
reset_counters();
|
||||
let start_stats = get_memory_stats();
|
||||
|
||||
|
||||
let mut decimals = Vec::new();
|
||||
for i in 0..1000 {
|
||||
let decimal = Decimal::from_str(&format!("0.{:04}", i)).unwrap();
|
||||
decimals.push(decimal);
|
||||
}
|
||||
|
||||
|
||||
let end_stats = get_memory_stats();
|
||||
let decimal_memory = end_stats.0 - start_stats.0;
|
||||
let decimal_allocs = end_stats.1 - start_stats.1;
|
||||
|
||||
|
||||
println!("📈 Decimal operations (1000 values):");
|
||||
println!(" {} allocs, {} bytes allocated", decimal_allocs, decimal_memory);
|
||||
|
||||
println!(
|
||||
" {} allocs, {} bytes allocated",
|
||||
decimal_allocs, decimal_memory
|
||||
);
|
||||
|
||||
// Test fixed-point operations
|
||||
reset_counters();
|
||||
let start_stats = get_memory_stats();
|
||||
|
||||
|
||||
let mut fixed_points = Vec::new();
|
||||
for i in 0..1000 {
|
||||
let fixed_point = (i as u32) * 10000; // Scale factor of 10000
|
||||
fixed_points.push(fixed_point);
|
||||
}
|
||||
|
||||
|
||||
let end_stats = get_memory_stats();
|
||||
let fixed_memory = end_stats.0 - start_stats.0;
|
||||
let fixed_allocs = end_stats.1 - start_stats.1;
|
||||
|
||||
|
||||
println!("📈 Fixed-point operations (1000 values):");
|
||||
println!(" {} allocs, {} bytes allocated", fixed_allocs, fixed_memory);
|
||||
|
||||
println!(
|
||||
" {} allocs, {} bytes allocated",
|
||||
fixed_allocs, fixed_memory
|
||||
);
|
||||
|
||||
if decimal_memory > 0 && fixed_memory > 0 {
|
||||
println!("📊 Fixed-point vs Decimal: {:.1}x less memory",
|
||||
decimal_memory as f64 / fixed_memory as f64);
|
||||
println!(
|
||||
"📊 Fixed-point vs Decimal: {:.1}x less memory",
|
||||
decimal_memory as f64 / fixed_memory as f64
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
println!("\n🎯 Memory Benchmark Summary");
|
||||
println!("==========================");
|
||||
println!("Key Findings:");
|
||||
@@ -238,8 +277,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" • Fixed-point arithmetic: Significantly less memory than Decimal");
|
||||
println!(" • JSON parsing: Efficient deserialization");
|
||||
println!(" • Network operations: Memory usage dominated by response size");
|
||||
|
||||
|
||||
println!("\nNote: These are ACTUAL measured values, not estimates!");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,18 +5,24 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 Network Optimization Test - polyfill-rs");
|
||||
println!("===========================================");
|
||||
|
||||
|
||||
// Test different client configurations
|
||||
let clients = vec![
|
||||
("Standard", ClobClient::new("https://clob.polymarket.com")),
|
||||
("Colocated", ClobClient::new_colocated("https://clob.polymarket.com")),
|
||||
("Internet", ClobClient::new_internet("https://clob.polymarket.com")),
|
||||
(
|
||||
"Colocated",
|
||||
ClobClient::new_colocated("https://clob.polymarket.com"),
|
||||
),
|
||||
(
|
||||
"Internet",
|
||||
ClobClient::new_internet("https://clob.polymarket.com"),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
for (name, client) in clients {
|
||||
println!("\n📊 Testing {} Client Configuration", name);
|
||||
println!("{}=", "=".repeat(40 + name.len()));
|
||||
|
||||
|
||||
// Test 1: Server time (baseline latency)
|
||||
println!(" 🔍 Server Time Test:");
|
||||
let mut times = Vec::new();
|
||||
@@ -27,36 +33,42 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} in {:?}", i+1, timestamp, duration);
|
||||
println!(" Run {}: ✅ {} in {:?}", i + 1, timestamp, duration);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
let std_dev = {
|
||||
let mean = avg.as_millis() as f64;
|
||||
let variance = times.iter()
|
||||
let variance = times
|
||||
.iter()
|
||||
.map(|t| (t.as_millis() as f64 - mean).powi(2))
|
||||
.sum::<f64>() / times.len() as f64;
|
||||
.sum::<f64>()
|
||||
/ times.len() as f64;
|
||||
variance.sqrt()
|
||||
};
|
||||
|
||||
println!(" 📈 Average: {:.1}ms ± {:.1}ms", avg.as_millis(), std_dev);
|
||||
|
||||
println!(
|
||||
" 📈 Average: {:.1}ms ± {:.1}ms",
|
||||
avg.as_millis(),
|
||||
std_dev
|
||||
);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🌐 Best: {:?}", min);
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Market data fetching
|
||||
println!(" 🔍 Market Data Test:");
|
||||
let mut times = Vec::new();
|
||||
@@ -67,29 +79,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 Average: {:?}", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🌐 Best: {:?}", min);
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Connection reuse test
|
||||
println!(" 🔍 Connection Reuse Test:");
|
||||
let start = Instant::now();
|
||||
@@ -99,18 +116,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if i == 0 {
|
||||
println!(" First request: {:?}", start.elapsed());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!(" Error on request {}: {}", i+1, e);
|
||||
println!(" Error on request {}: {}", i + 1, e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
let total_time = start.elapsed();
|
||||
println!(" 📈 5 requests total: {:?}", total_time);
|
||||
println!(" 📊 Average per request: {:?}", total_time / 5);
|
||||
}
|
||||
|
||||
|
||||
println!("\n🎯 Network Optimization Summary");
|
||||
println!("===============================");
|
||||
println!("HTTP Client Optimizations Applied:");
|
||||
@@ -119,17 +136,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" • HTTP/2 with keep-alive");
|
||||
println!(" • Optimized timeouts for different environments");
|
||||
println!(" • Compression enabled/disabled based on use case");
|
||||
|
||||
|
||||
println!("\nConfiguration Recommendations:");
|
||||
println!(" • Colocated: Use for servers close to exchange");
|
||||
println!(" • Internet: Use for retail/remote connections");
|
||||
println!(" • Standard: Balanced settings for most use cases");
|
||||
|
||||
|
||||
println!("\nAdditional Optimizations Available:");
|
||||
println!(" • Custom DNS resolver");
|
||||
println!(" • Connection pre-warming");
|
||||
println!(" • Request batching");
|
||||
println!(" • Circuit breaker patterns");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🌐 Network Latency Test for polyfill-rs");
|
||||
println!("======================================");
|
||||
|
||||
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Test 1: Simplified markets (comparable to original 404.5ms benchmark)
|
||||
println!("\n📊 Test 1: Simplified Markets");
|
||||
println!("-----------------------------");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..5 {
|
||||
let start = Instant::now();
|
||||
@@ -19,31 +19,38 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(markets) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
}
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
}
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 Average: {:?}", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🆚 vs original (404.5ms): {:.1}x",
|
||||
404.5 / avg.as_millis() as f64);
|
||||
println!(
|
||||
" 🆚 vs original (404.5ms): {:.1}x",
|
||||
404.5 / avg.as_millis() as f64
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Full markets
|
||||
println!("\n📊 Test 2: Full Markets");
|
||||
println!("----------------------");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..3 {
|
||||
let start = Instant::now();
|
||||
@@ -51,29 +58,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(markets) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
}
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
}
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 Average: {:?}", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Server time (lightweight endpoint)
|
||||
println!("\n📊 Test 3: Server Time (Lightweight)");
|
||||
println!("-----------------------------------");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..10 {
|
||||
let start = Instant::now();
|
||||
@@ -82,27 +94,32 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i == 0 {
|
||||
println!(" Run {}: ✅ Timestamp {} in {:?}", i+1, timestamp, duration);
|
||||
println!(
|
||||
" Run {}: ✅ Timestamp {} in {:?}",
|
||||
i + 1,
|
||||
timestamp,
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
}
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 Average: {:?}", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🌐 Network baseline latency: ~{:?}", min);
|
||||
}
|
||||
|
||||
|
||||
println!("\n🎯 Summary");
|
||||
println!("=========");
|
||||
println!("Network latency dominates end-to-end performance.");
|
||||
@@ -115,6 +132,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("• Run from same geographic location");
|
||||
println!("• Use same network conditions");
|
||||
println!("• Measure full end-to-end latency");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🔐 Proper Authenticated Benchmark - Real Performance");
|
||||
println!("===================================================");
|
||||
|
||||
|
||||
// Note: For a real benchmark, we'd need:
|
||||
// 1. A private key to initialize the signer
|
||||
// 2. Proper API credential setup
|
||||
// 3. Valid market/token IDs
|
||||
|
||||
|
||||
println!("⚠️ Authentication Setup Required");
|
||||
println!("================================");
|
||||
println!("To get real order creation benchmarks, we need:");
|
||||
@@ -18,13 +18,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" 2. Proper client initialization with credentials");
|
||||
println!(" 3. Valid market context for orders");
|
||||
println!();
|
||||
|
||||
|
||||
// What we CAN measure: Network performance
|
||||
let client = ClobClient::new_internet("https://clob.polymarket.com");
|
||||
|
||||
|
||||
println!("📊 What We CAN Measure: Network Performance");
|
||||
println!("==========================================");
|
||||
|
||||
|
||||
// Test 1: Basic connectivity (network baseline)
|
||||
println!("\n🔍 Network Baseline Test:");
|
||||
let mut baseline_times = Vec::new();
|
||||
@@ -33,24 +33,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let result = client.get_server_time().await;
|
||||
let duration = start.elapsed();
|
||||
baseline_times.push(duration);
|
||||
|
||||
|
||||
match result {
|
||||
Ok(timestamp) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ Server time {} in {:?}", i+1, timestamp, duration);
|
||||
println!(
|
||||
" Run {}: ✅ Server time {} in {:?}",
|
||||
i + 1,
|
||||
timestamp,
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let baseline_avg = baseline_times.iter().sum::<std::time::Duration>() / baseline_times.len() as u32;
|
||||
|
||||
let baseline_avg =
|
||||
baseline_times.iter().sum::<std::time::Duration>() / baseline_times.len() as u32;
|
||||
println!(" 📈 Network baseline: {:?}", baseline_avg);
|
||||
|
||||
|
||||
// Test 2: Market data (what we successfully measured before)
|
||||
println!("\n🔍 Market Data Performance:");
|
||||
let mut market_times = Vec::new();
|
||||
@@ -59,42 +65,57 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let result = client.get_sampling_simplified_markets(None).await;
|
||||
let duration = start.elapsed();
|
||||
market_times.push(duration);
|
||||
|
||||
|
||||
match result {
|
||||
Ok(markets) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let market_avg = market_times.iter().sum::<std::time::Duration>() / market_times.len() as u32;
|
||||
println!(" 📈 Market data average: {:?}", market_avg);
|
||||
println!(" 🆚 vs original (404.5ms): {:.1}x faster", 404.5 / market_avg.as_millis() as f64);
|
||||
|
||||
println!(
|
||||
" 🆚 vs original (404.5ms): {:.1}x faster",
|
||||
404.5 / market_avg.as_millis() as f64
|
||||
);
|
||||
|
||||
println!("\n🎯 Realistic Performance Estimates");
|
||||
println!("=================================");
|
||||
|
||||
|
||||
println!("Based on our network measurements:");
|
||||
println!(" • Network baseline: {:?}", baseline_avg);
|
||||
println!(" • Market data: {:?} (3.8x faster than original)", market_avg);
|
||||
println!(
|
||||
" • Market data: {:?} (3.8x faster than original)",
|
||||
market_avg
|
||||
);
|
||||
println!();
|
||||
|
||||
|
||||
println!("For order creation (266.5ms original):");
|
||||
println!(" • Network component: ~{:?} (measured)", baseline_avg);
|
||||
println!(" • EIP-712 signing: ~5-20ms (typical crypto operation)");
|
||||
println!(" • JSON serialization: ~1ms (measured separately)");
|
||||
println!(" • Estimated total: ~{:?} (vs 266.5ms original)",
|
||||
baseline_avg + std::time::Duration::from_millis(15));
|
||||
println!(" • Estimated improvement: {:.1}x faster",
|
||||
266.5 / (baseline_avg.as_millis() + 15) as f64);
|
||||
|
||||
println!(
|
||||
" • Estimated total: ~{:?} (vs 266.5ms original)",
|
||||
baseline_avg + std::time::Duration::from_millis(15)
|
||||
);
|
||||
println!(
|
||||
" • Estimated improvement: {:.1}x faster",
|
||||
266.5 / (baseline_avg.as_millis() + 15) as f64
|
||||
);
|
||||
|
||||
println!("\n📊 Summary of Real Performance");
|
||||
println!("=============================");
|
||||
println!("What we measured:");
|
||||
@@ -103,11 +124,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" ✅ Computational: microsecond-scale operations");
|
||||
println!();
|
||||
println!("What we estimate:");
|
||||
println!(" 📊 Order creation: ~{:?} (vs 266.5ms = 2.2x faster)",
|
||||
baseline_avg + std::time::Duration::from_millis(15));
|
||||
println!(
|
||||
" 📊 Order creation: ~{:?} (vs 266.5ms = 2.2x faster)",
|
||||
baseline_avg + std::time::Duration::from_millis(15)
|
||||
);
|
||||
println!(" 📊 All operations benefit from 11% network optimization");
|
||||
println!(" 📊 Connection reuse provides 70% improvement on subsequent calls");
|
||||
println!(" 📊 Request batching provides 200% improvement for parallel operations");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+101
-76
@@ -3,23 +3,23 @@
|
||||
//! This example demonstrates all available API endpoints in a simple, easy-to-run format.
|
||||
//! It can be run without authentication credentials and will test all public endpoints.
|
||||
|
||||
use polyfill_rs::{ClobClient, Side, Result, PolyfillError};
|
||||
use polyfill_rs::{ClobClient, PolyfillError, Result, Side};
|
||||
use rust_decimal::Decimal;
|
||||
use tokio::time::{sleep, Duration};
|
||||
use tracing::{info, error, warn};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
/// Quick demo that tests all available endpoints
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize logging
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
|
||||
info!("Polyfill-rs Quick Demo");
|
||||
info!("======================");
|
||||
|
||||
|
||||
// Create client
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Test 1: Basic connectivity
|
||||
info!("\nTesting API Connectivity...");
|
||||
match test_connectivity(&client).await {
|
||||
@@ -27,37 +27,37 @@ async fn main() -> Result<()> {
|
||||
Err(e) => {
|
||||
error!("API connectivity test failed: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Get a valid token ID from markets
|
||||
info!("\nGetting Market Data...");
|
||||
let token_id = match get_valid_token_id(&client).await {
|
||||
Ok(id) => {
|
||||
info!("Found valid token ID: {}", id);
|
||||
id
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to get valid token ID: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Test 3: Test all market data endpoints
|
||||
info!("\nTesting Market Data Endpoints...");
|
||||
test_market_data_endpoints(&client, &token_id).await?;
|
||||
|
||||
|
||||
// Test 4: Test error handling
|
||||
info!("\nTesting Error Handling...");
|
||||
test_error_handling(&client).await?;
|
||||
|
||||
|
||||
// Test 5: Performance test
|
||||
info!("\nTesting Performance...");
|
||||
test_performance(&client, &token_id).await?;
|
||||
|
||||
|
||||
info!("\nAll tests completed successfully!");
|
||||
info!("The polyfill-rs client is working correctly with the Polymarket API.");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -66,39 +66,43 @@ async fn test_connectivity(client: &ClobClient) -> Result<()> {
|
||||
// Test /ok endpoint
|
||||
let is_ok = client.get_ok().await;
|
||||
if !is_ok {
|
||||
return Err(PolyfillError::network("API not responding", std::io::Error::other("API not responding")));
|
||||
return Err(PolyfillError::network(
|
||||
"API not responding",
|
||||
std::io::Error::other("API not responding"),
|
||||
));
|
||||
}
|
||||
info!(" /ok endpoint responding");
|
||||
|
||||
|
||||
// Test /time endpoint
|
||||
let server_time = client.get_server_time().await?;
|
||||
info!(" Server time: {}", server_time);
|
||||
|
||||
|
||||
// Verify server time is reasonable (within last 24 hours)
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
|
||||
let time_diff = server_time.abs_diff(current_time);
|
||||
|
||||
if time_diff > 86400 { // 24 hours
|
||||
|
||||
if time_diff > 86400 {
|
||||
// 24 hours
|
||||
warn!(" Server time seems off (diff: {} seconds)", time_diff);
|
||||
} else {
|
||||
info!(" Server time is reasonable");
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get a valid token ID from the markets endpoint
|
||||
async fn get_valid_token_id(client: &ClobClient) -> Result<String> {
|
||||
let markets = client.get_sampling_markets(None).await?;
|
||||
|
||||
|
||||
if markets.data.is_empty() {
|
||||
return Err(PolyfillError::api(404, "No markets found"));
|
||||
}
|
||||
|
||||
|
||||
// Find a market with active tokens
|
||||
for market in &markets.data {
|
||||
if market.active && !market.closed {
|
||||
@@ -113,8 +117,11 @@ async fn get_valid_token_id(client: &ClobClient) -> Result<String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(PolyfillError::api(404, "No active markets with valid tokens found"))
|
||||
|
||||
Err(PolyfillError::api(
|
||||
404,
|
||||
"No active markets with valid tokens found",
|
||||
))
|
||||
}
|
||||
|
||||
/// Test all market data endpoints
|
||||
@@ -122,42 +129,46 @@ async fn test_market_data_endpoints(client: &ClobClient, token_id: &str) -> Resu
|
||||
// Test order book
|
||||
info!(" Testing order book endpoint...");
|
||||
let order_book = client.get_order_book(token_id).await?;
|
||||
info!(" Order book: {} bids, {} asks", order_book.bids.len(), order_book.asks.len());
|
||||
|
||||
info!(
|
||||
" Order book: {} bids, {} asks",
|
||||
order_book.bids.len(),
|
||||
order_book.asks.len()
|
||||
);
|
||||
|
||||
// Test midpoint
|
||||
info!(" Testing midpoint endpoint...");
|
||||
let midpoint = client.get_midpoint(token_id).await?;
|
||||
info!(" Midpoint: {}", midpoint.mid);
|
||||
|
||||
|
||||
// Test spread
|
||||
info!(" Testing spread endpoint...");
|
||||
let spread = client.get_spread(token_id).await?;
|
||||
info!(" Spread: {}", spread.spread);
|
||||
|
||||
|
||||
// Test buy price
|
||||
info!(" Testing buy price endpoint...");
|
||||
let buy_price = client.get_price(token_id, Side::BUY).await?;
|
||||
info!(" Buy price: {}", buy_price.price);
|
||||
|
||||
|
||||
// Test sell price
|
||||
info!(" Testing sell price endpoint...");
|
||||
let sell_price = client.get_price(token_id, Side::SELL).await?;
|
||||
info!(" Sell price: {}", sell_price.price);
|
||||
|
||||
|
||||
// Test tick size
|
||||
info!(" Testing tick size endpoint...");
|
||||
let tick_size = client.get_tick_size(token_id).await?;
|
||||
info!(" Tick size: {}", tick_size);
|
||||
|
||||
|
||||
// Test neg risk
|
||||
info!(" Testing neg risk endpoint...");
|
||||
let neg_risk = client.get_neg_risk(token_id).await?;
|
||||
info!(" Neg risk: {}", neg_risk);
|
||||
|
||||
|
||||
// Validate data consistency
|
||||
info!(" Validating data consistency...");
|
||||
validate_market_data(&order_book, &midpoint, &spread, &buy_price, &sell_price)?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -175,37 +186,39 @@ fn validate_market_data(
|
||||
} else {
|
||||
info!(" Order book has liquidity");
|
||||
}
|
||||
|
||||
|
||||
// Check that prices are positive
|
||||
if buy_price.price <= Decimal::ZERO {
|
||||
warn!(" Buy price is not positive: {}", buy_price.price);
|
||||
} else {
|
||||
info!(" Buy price is positive");
|
||||
}
|
||||
|
||||
|
||||
if sell_price.price <= Decimal::ZERO {
|
||||
warn!(" Sell price is not positive: {}", sell_price.price);
|
||||
} else {
|
||||
info!(" Sell price is positive");
|
||||
}
|
||||
|
||||
|
||||
// Check that spread is reasonable
|
||||
if spread.spread < Decimal::ZERO {
|
||||
warn!(" Spread is negative: {}", spread.spread);
|
||||
} else {
|
||||
info!(" Spread is non-negative");
|
||||
}
|
||||
|
||||
|
||||
// Check that midpoint is between buy and sell prices (if both exist)
|
||||
if buy_price.price > Decimal::ZERO && sell_price.price > Decimal::ZERO {
|
||||
if midpoint.mid < buy_price.price || midpoint.mid > sell_price.price {
|
||||
warn!(" Midpoint {} is not between buy {} and sell {}",
|
||||
midpoint.mid, buy_price.price, sell_price.price);
|
||||
warn!(
|
||||
" Midpoint {} is not between buy {} and sell {}",
|
||||
midpoint.mid, buy_price.price, sell_price.price
|
||||
);
|
||||
} else {
|
||||
info!(" Midpoint is between buy and sell prices");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -217,35 +230,33 @@ async fn test_error_handling(client: &ClobClient) -> Result<()> {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
warn!(" Invalid token ID returned data instead of error");
|
||||
}
|
||||
Err(e) => {
|
||||
match e {
|
||||
PolyfillError::Api { status, .. } => {
|
||||
if status >= 400 {
|
||||
info!(" Invalid token ID correctly returned error: {}", status);
|
||||
} else {
|
||||
warn!(" Unexpected status code for invalid token: {}", status);
|
||||
}
|
||||
},
|
||||
Err(e) => match e {
|
||||
PolyfillError::Api { status, .. } => {
|
||||
if status >= 400 {
|
||||
info!(" Invalid token ID correctly returned error: {}", status);
|
||||
} else {
|
||||
warn!(" Unexpected status code for invalid token: {}", status);
|
||||
}
|
||||
_ => {
|
||||
info!(" Invalid token ID returned error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
info!(" Invalid token ID returned error: {:?}", e);
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Test with empty token ID
|
||||
info!(" Testing empty token ID...");
|
||||
let result = client.get_order_book("").await;
|
||||
match result {
|
||||
Ok(_) => {
|
||||
warn!(" Empty token ID returned data instead of error");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
info!(" Empty token ID correctly returned error: {:?}", e);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -254,56 +265,70 @@ async fn test_performance(client: &ClobClient, token_id: &str) -> Result<()> {
|
||||
let mut total_time = Duration::from_secs(0);
|
||||
let mut success_count = 0;
|
||||
let test_count = 5;
|
||||
|
||||
|
||||
info!(" Running {} performance tests...", test_count);
|
||||
|
||||
|
||||
for i in 1..=test_count {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
|
||||
// Test a mix of endpoints
|
||||
let results = tokio::join!(
|
||||
client.get_server_time(),
|
||||
client.get_midpoint(token_id),
|
||||
client.get_spread(token_id),
|
||||
);
|
||||
|
||||
|
||||
let duration = start.elapsed();
|
||||
total_time += duration;
|
||||
|
||||
|
||||
match results {
|
||||
(Ok(_), Ok(_), Ok(_)) => {
|
||||
success_count += 1;
|
||||
info!(" Test {}: PASSED {:.2}ms", i, duration.as_secs_f64() * 1000.0);
|
||||
}
|
||||
info!(
|
||||
" Test {}: PASSED {:.2}ms",
|
||||
i,
|
||||
duration.as_secs_f64() * 1000.0
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
warn!(" Test {}: FAILED in {:.2}ms", i, duration.as_secs_f64() * 1000.0);
|
||||
}
|
||||
warn!(
|
||||
" Test {}: FAILED in {:.2}ms",
|
||||
i,
|
||||
duration.as_secs_f64() * 1000.0
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Small delay between tests
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
|
||||
let avg_time = total_time / test_count as u32;
|
||||
let success_rate = (success_count as f64 / test_count as f64) * 100.0;
|
||||
|
||||
|
||||
info!(" Performance Summary:");
|
||||
info!(" Success rate: {:.1}%", success_rate);
|
||||
info!(" Average response time: {:.2}ms", avg_time.as_secs_f64() * 1000.0);
|
||||
info!(
|
||||
" Average response time: {:.2}ms",
|
||||
avg_time.as_secs_f64() * 1000.0
|
||||
);
|
||||
info!(" Total time: {:.2}s", total_time.as_secs_f64());
|
||||
|
||||
|
||||
// Performance thresholds
|
||||
if avg_time > Duration::from_secs(2) {
|
||||
warn!(" Average response time is slow: {:.2}ms", avg_time.as_secs_f64() * 1000.0);
|
||||
warn!(
|
||||
" Average response time is slow: {:.2}ms",
|
||||
avg_time.as_secs_f64() * 1000.0
|
||||
);
|
||||
} else {
|
||||
info!(" Response times are acceptable");
|
||||
}
|
||||
|
||||
|
||||
if success_rate < 80.0 {
|
||||
warn!(" Success rate is low: {:.1}%", success_rate);
|
||||
} else {
|
||||
info!(" Success rate is good");
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Load environment variables from .env file
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
|
||||
println!("🚀 Real Network Benchmark - polyfill-rs vs polymarket-rs-client");
|
||||
println!("================================================================");
|
||||
|
||||
|
||||
// Set up client with credentials
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// API credentials from .env file
|
||||
let _api_key = std::env::var("POLYMARKET_API_KEY")
|
||||
.map_err(|_| "POLYMARKET_API_KEY not found in .env file")?;
|
||||
@@ -21,16 +21,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.map_err(|_| "POLYMARKET_SECRET not found in .env file")?;
|
||||
let _passphrase = std::env::var("POLYMARKET_PASSPHRASE")
|
||||
.map_err(|_| "POLYMARKET_PASSPHRASE not found in .env file")?;
|
||||
|
||||
|
||||
println!("✅ Loaded API credentials from .env file");
|
||||
|
||||
|
||||
println!("🔑 Using API credentials for authenticated requests");
|
||||
|
||||
|
||||
// Test 1: Simplified Markets (matches original 404.5ms benchmark)
|
||||
println!("\n📊 Test 1: Fetch Simplified Markets");
|
||||
println!("===================================");
|
||||
println!("Original polymarket-rs-client: 404.5ms ± 22.9ms");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..10 {
|
||||
let start = Instant::now();
|
||||
@@ -39,42 +39,59 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 3 {
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 3 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
let std_dev = {
|
||||
let mean = avg.as_millis() as f64;
|
||||
let variance = times.iter()
|
||||
let variance = times
|
||||
.iter()
|
||||
.map(|t| (t.as_millis() as f64 - mean).powi(2))
|
||||
.sum::<f64>() / times.len() as f64;
|
||||
.sum::<f64>()
|
||||
/ times.len() as f64;
|
||||
variance.sqrt()
|
||||
};
|
||||
|
||||
println!(" 📈 polyfill-rs: {:.1}ms ± {:.1}ms", avg.as_millis(), std_dev);
|
||||
|
||||
println!(
|
||||
" 📈 polyfill-rs: {:.1}ms ± {:.1}ms",
|
||||
avg.as_millis(),
|
||||
std_dev
|
||||
);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🆚 vs original: {:.1}x {}",
|
||||
404.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 405 { "faster" } else { "slower" });
|
||||
println!(
|
||||
" 🆚 vs original: {:.1}x {}",
|
||||
404.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 405 {
|
||||
"faster"
|
||||
} else {
|
||||
"slower"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Full Markets (no direct comparison, but good to measure)
|
||||
println!("\n📊 Test 2: Fetch Full Markets");
|
||||
println!("=============================");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..5 {
|
||||
let start = Instant::now();
|
||||
@@ -83,38 +100,43 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} markets in {:?}", i+1, markets.data.len(), duration);
|
||||
println!(
|
||||
" Run {}: ✅ {} markets in {:?}",
|
||||
i + 1,
|
||||
markets.data.len(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 polyfill-rs: {:?} average", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Order Creation with EIP-712 (matches original 266.5ms benchmark)
|
||||
println!("\n📊 Test 3: Create Order with EIP-712 Signature");
|
||||
println!("==============================================");
|
||||
println!("Original polymarket-rs-client: 266.5ms ± 28.6ms");
|
||||
|
||||
|
||||
// First, try to create or derive API key
|
||||
match client.create_or_derive_api_key(None).await {
|
||||
Ok(_creds) => {
|
||||
println!(" 🔑 API credentials set up successfully");
|
||||
|
||||
|
||||
// Now test order creation
|
||||
let mut times = Vec::new();
|
||||
for i in 0..5 {
|
||||
@@ -124,59 +146,71 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Decimal::from_str("1.0").unwrap(), // Minimum order size
|
||||
Side::BUY,
|
||||
);
|
||||
|
||||
|
||||
let start = Instant::now();
|
||||
match client.create_order(&order_args, None, None, None).await {
|
||||
Ok(_order) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ Order created in {:?}", i+1, duration);
|
||||
println!(" Run {}: ✅ Order created in {:?}", i + 1, duration);
|
||||
}
|
||||
|
||||
|
||||
// Cancel the order immediately to clean up
|
||||
// Note: Would need to extract order ID from response for cancellation
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
let std_dev = {
|
||||
let mean = avg.as_millis() as f64;
|
||||
let variance = times.iter()
|
||||
let variance = times
|
||||
.iter()
|
||||
.map(|t| (t.as_millis() as f64 - mean).powi(2))
|
||||
.sum::<f64>() / times.len() as f64;
|
||||
.sum::<f64>()
|
||||
/ times.len() as f64;
|
||||
variance.sqrt()
|
||||
};
|
||||
|
||||
println!(" 📈 polyfill-rs: {:.1}ms ± {:.1}ms", avg.as_millis(), std_dev);
|
||||
|
||||
println!(
|
||||
" 📈 polyfill-rs: {:.1}ms ± {:.1}ms",
|
||||
avg.as_millis(),
|
||||
std_dev
|
||||
);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🆚 vs original: {:.1}x {}",
|
||||
266.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 267 { "faster" } else { "slower" });
|
||||
println!(
|
||||
" 🆚 vs original: {:.1}x {}",
|
||||
266.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 267 {
|
||||
"faster"
|
||||
} else {
|
||||
"slower"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!(" ❌ Could not set up API credentials: {}", e);
|
||||
println!(" ⚠️ Skipping order creation benchmark");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Test 4: Memory usage comparison
|
||||
println!("\n📊 Test 4: Memory Usage Analysis");
|
||||
println!("===============================");
|
||||
println!("Original: 88,053 allocs, 81,823 frees, 15,945,966 bytes allocated");
|
||||
|
||||
|
||||
// This would require memory profiling tools for accurate measurement
|
||||
println!(" 🔧 polyfill-rs optimizations:");
|
||||
println!(" • Fixed-point arithmetic reduces allocation overhead");
|
||||
@@ -184,34 +218,38 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" • Zero-allocation order book updates");
|
||||
println!(" • Pre-allocated pools for high-frequency operations");
|
||||
println!(" 📈 Estimated: ~10x reduction in allocations");
|
||||
|
||||
|
||||
// Test 5: Computational performance (our strength)
|
||||
println!("\n📊 Test 5: Computational Performance");
|
||||
println!("===================================");
|
||||
|
||||
|
||||
use polyfill_rs::OrderBookImpl;
|
||||
|
||||
|
||||
let mut book = OrderBookImpl::new("test_token".to_string(), 100);
|
||||
|
||||
|
||||
// Order book updates
|
||||
let start = Instant::now();
|
||||
for i in 0..10000 {
|
||||
let price = Decimal::from_str(&format!("0.{:04}", 5000 + (i % 1000))).unwrap();
|
||||
let size = Decimal::from_str("100.0").unwrap();
|
||||
|
||||
|
||||
let delta = polyfill_rs::OrderDelta {
|
||||
token_id: "test_token".to_string(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: if i % 2 == 0 { polyfill_rs::Side::BUY } else { polyfill_rs::Side::SELL },
|
||||
side: if i % 2 == 0 {
|
||||
polyfill_rs::Side::BUY
|
||||
} else {
|
||||
polyfill_rs::Side::SELL
|
||||
},
|
||||
price,
|
||||
size,
|
||||
sequence: i as u64,
|
||||
};
|
||||
|
||||
|
||||
let _ = book.apply_delta(delta);
|
||||
}
|
||||
let book_duration = start.elapsed();
|
||||
|
||||
|
||||
// Fast calculations
|
||||
let start = Instant::now();
|
||||
for _ in 0..1000000 {
|
||||
@@ -219,12 +257,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let _ = book.mid_price_fast();
|
||||
}
|
||||
let calc_duration = start.elapsed();
|
||||
|
||||
println!(" ⚡ Order book updates: 10,000 in {:?} ({:.0} ops/sec)",
|
||||
book_duration, 10000.0 / book_duration.as_secs_f64());
|
||||
println!(" ⚡ Fast calculations: 2M in {:?} ({:.0}M ops/sec)",
|
||||
calc_duration, 2.0 / calc_duration.as_secs_f64());
|
||||
|
||||
|
||||
println!(
|
||||
" ⚡ Order book updates: 10,000 in {:?} ({:.0} ops/sec)",
|
||||
book_duration,
|
||||
10000.0 / book_duration.as_secs_f64()
|
||||
);
|
||||
println!(
|
||||
" ⚡ Fast calculations: 2M in {:?} ({:.0}M ops/sec)",
|
||||
calc_duration,
|
||||
2.0 / calc_duration.as_secs_f64()
|
||||
);
|
||||
|
||||
println!("\n🎯 Final Comparison Summary");
|
||||
println!("==========================");
|
||||
println!("| Metric | polymarket-rs-client | polyfill-rs | Improvement |");
|
||||
@@ -234,13 +278,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("| Order book ops | N/A | ~1µs per update | New capability |");
|
||||
println!("| Fast calculations | N/A | ~500ns per op | New capability |");
|
||||
println!("| Memory usage | 15.9MB allocated | ~10x less | Significant |");
|
||||
|
||||
|
||||
println!("\n✨ Key Advantages of polyfill-rs:");
|
||||
println!(" • Competitive network performance");
|
||||
println!(" • Superior computational performance");
|
||||
println!(" • Memory-efficient data structures");
|
||||
println!(" • Zero-allocation hot paths");
|
||||
println!(" • Fixed-point arithmetic optimizations");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ use std::time::Instant;
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 Simple Network Benchmark - polyfill-rs");
|
||||
println!("==========================================");
|
||||
|
||||
|
||||
let client = ClobClient::new("https://clob.polymarket.com");
|
||||
|
||||
|
||||
// Test 1: Server Time (baseline network latency)
|
||||
println!("\n📊 Test 1: Server Time (Network Baseline)");
|
||||
println!("=========================================");
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..10 {
|
||||
let start = Instant::now();
|
||||
@@ -20,52 +20,54 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
if i < 3 {
|
||||
println!(" Run {}: ✅ {} in {:?}", i+1, timestamp, duration);
|
||||
println!(" Run {}: ✅ {} in {:?}", i + 1, timestamp, duration);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
}
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
|
||||
|
||||
println!(" 📈 Average: {:?}", avg);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
println!(" 🌐 Network baseline: ~{:?}", min);
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Market Data (comparable to original benchmarks)
|
||||
println!("\n📊 Test 2: Market Data Fetching");
|
||||
println!("===============================");
|
||||
println!("Target: polymarket-rs-client 404.5ms ± 22.9ms");
|
||||
|
||||
|
||||
// Try different endpoints to see which ones work
|
||||
let endpoints = vec![
|
||||
("Simplified Markets", "get_sampling_simplified_markets"),
|
||||
("Full Markets", "get_sampling_markets"),
|
||||
("Market Prices", "get_prices_batch"),
|
||||
];
|
||||
|
||||
|
||||
for (name, _method) in endpoints {
|
||||
println!("\n 🔍 Testing {}:", name);
|
||||
|
||||
|
||||
let mut times = Vec::new();
|
||||
for i in 0..5 {
|
||||
let start = Instant::now();
|
||||
let result = match name {
|
||||
"Simplified Markets" => {
|
||||
client.get_sampling_simplified_markets(None).await.map(|r| r.data.len())
|
||||
}
|
||||
"Full Markets" => {
|
||||
client.get_sampling_markets(None).await.map(|r| r.data.len())
|
||||
}
|
||||
"Simplified Markets" => client
|
||||
.get_sampling_simplified_markets(None)
|
||||
.await
|
||||
.map(|r| r.data.len()),
|
||||
"Full Markets" => client
|
||||
.get_sampling_markets(None)
|
||||
.await
|
||||
.map(|r| r.data.len()),
|
||||
"Market Prices" => {
|
||||
// Try with some example BookParams
|
||||
let book_params = vec![
|
||||
@@ -75,96 +77,116 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
];
|
||||
client.get_prices(&book_params).await.map(|r| r.len())
|
||||
}
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
|
||||
let duration = start.elapsed();
|
||||
times.push(duration);
|
||||
|
||||
|
||||
match result {
|
||||
Ok(count) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ✅ {} items in {:?}", i+1, count, duration);
|
||||
println!(" Run {}: ✅ {} items in {:?}", i + 1, count, duration);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
if i < 2 {
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i+1, duration, e);
|
||||
println!(" Run {}: ❌ Error in {:?}: {}", i + 1, duration, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !times.is_empty() {
|
||||
let avg = times.iter().sum::<std::time::Duration>() / times.len() as u32;
|
||||
let min = times.iter().min().unwrap();
|
||||
let max = times.iter().max().unwrap();
|
||||
let std_dev = {
|
||||
let mean = avg.as_millis() as f64;
|
||||
let variance = times.iter()
|
||||
let variance = times
|
||||
.iter()
|
||||
.map(|t| (t.as_millis() as f64 - mean).powi(2))
|
||||
.sum::<f64>() / times.len() as f64;
|
||||
.sum::<f64>()
|
||||
/ times.len() as f64;
|
||||
variance.sqrt()
|
||||
};
|
||||
|
||||
println!(" 📈 polyfill-rs: {:.1}ms ± {:.1}ms", avg.as_millis(), std_dev);
|
||||
|
||||
println!(
|
||||
" 📈 polyfill-rs: {:.1}ms ± {:.1}ms",
|
||||
avg.as_millis(),
|
||||
std_dev
|
||||
);
|
||||
println!(" 📊 Range: {:?} - {:?}", min, max);
|
||||
|
||||
|
||||
if name == "Simplified Markets" {
|
||||
println!(" 🆚 vs original (404.5ms): {:.1}x {}",
|
||||
404.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 405 { "faster" } else { "slower" });
|
||||
println!(
|
||||
" 🆚 vs original (404.5ms): {:.1}x {}",
|
||||
404.5 / avg.as_millis() as f64,
|
||||
if avg.as_millis() < 405 {
|
||||
"faster"
|
||||
} else {
|
||||
"slower"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Computational Performance (our strength)
|
||||
println!("\n📊 Test 3: Computational Performance");
|
||||
println!("===================================");
|
||||
|
||||
|
||||
use polyfill_rs::OrderBookImpl;
|
||||
use rust_decimal::Decimal;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
let mut book = OrderBookImpl::new("test_token".to_string(), 100);
|
||||
|
||||
|
||||
// Populate the book first
|
||||
for i in 0..100 {
|
||||
let price = Decimal::from_str(&format!("0.{:04}", 5000 + i)).unwrap();
|
||||
let size = Decimal::from_str("100.0").unwrap();
|
||||
|
||||
|
||||
let delta = polyfill_rs::OrderDelta {
|
||||
token_id: "test_token".to_string(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: if i % 2 == 0 { polyfill_rs::Side::BUY } else { polyfill_rs::Side::SELL },
|
||||
side: if i % 2 == 0 {
|
||||
polyfill_rs::Side::BUY
|
||||
} else {
|
||||
polyfill_rs::Side::SELL
|
||||
},
|
||||
price,
|
||||
size,
|
||||
sequence: i as u64,
|
||||
};
|
||||
|
||||
|
||||
let _ = book.apply_delta(delta);
|
||||
}
|
||||
|
||||
|
||||
// Benchmark order book updates
|
||||
let start = Instant::now();
|
||||
for i in 0..10000 {
|
||||
let price = Decimal::from_str(&format!("0.{:04}", 5000 + (i % 1000))).unwrap();
|
||||
let size = Decimal::from_str("100.0").unwrap();
|
||||
|
||||
|
||||
let delta = polyfill_rs::OrderDelta {
|
||||
token_id: "test_token".to_string(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: if i % 2 == 0 { polyfill_rs::Side::BUY } else { polyfill_rs::Side::SELL },
|
||||
side: if i % 2 == 0 {
|
||||
polyfill_rs::Side::BUY
|
||||
} else {
|
||||
polyfill_rs::Side::SELL
|
||||
},
|
||||
price,
|
||||
size,
|
||||
sequence: (i + 1000) as u64,
|
||||
};
|
||||
|
||||
|
||||
let _ = book.apply_delta(delta);
|
||||
}
|
||||
let book_duration = start.elapsed();
|
||||
|
||||
|
||||
// Benchmark fast calculations
|
||||
let start = Instant::now();
|
||||
for _ in 0..1000000 {
|
||||
@@ -172,17 +194,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let _ = book.mid_price_fast();
|
||||
}
|
||||
let calc_duration = start.elapsed();
|
||||
|
||||
|
||||
println!(" ⚡ Order book: 10,000 updates in {:?}", book_duration);
|
||||
println!(" 📊 Rate: {:.0} updates/second", 10000.0 / book_duration.as_secs_f64());
|
||||
|
||||
println!(
|
||||
" 📊 Rate: {:.0} updates/second",
|
||||
10000.0 / book_duration.as_secs_f64()
|
||||
);
|
||||
|
||||
println!(" ⚡ Fast calcs: 2M operations in {:?}", calc_duration);
|
||||
println!(" 📊 Rate: {:.0}M operations/second", 2.0 / calc_duration.as_secs_f64());
|
||||
|
||||
println!(
|
||||
" 📊 Rate: {:.0}M operations/second",
|
||||
2.0 / calc_duration.as_secs_f64()
|
||||
);
|
||||
|
||||
// Test 4: JSON Parsing Performance
|
||||
println!("\n📊 Test 4: JSON Parsing Performance");
|
||||
println!("==================================");
|
||||
|
||||
|
||||
let sample_market_json = r#"{
|
||||
"condition_id": "21742633143463906290569050155826241533067272736897614950488156847949938836455",
|
||||
"question": "Will Donald Trump win the 2024 US Presidential Election?",
|
||||
@@ -213,35 +241,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
|
||||
let start = Instant::now();
|
||||
for _ in 0..10000 {
|
||||
let _: Result<serde_json::Value, _> = serde_json::from_str(sample_market_json);
|
||||
}
|
||||
let json_duration = start.elapsed();
|
||||
|
||||
|
||||
println!(" ⚡ JSON parsing: 10,000 parses in {:?}", json_duration);
|
||||
println!(" 📊 Rate: {:.0} parses/second", 10000.0 / json_duration.as_secs_f64());
|
||||
println!(" 📊 Per parse: {:.1}µs", json_duration.as_micros() as f64 / 10000.0);
|
||||
|
||||
println!(
|
||||
" 📊 Rate: {:.0} parses/second",
|
||||
10000.0 / json_duration.as_secs_f64()
|
||||
);
|
||||
println!(
|
||||
" 📊 Per parse: {:.1}µs",
|
||||
json_duration.as_micros() as f64 / 10000.0
|
||||
);
|
||||
|
||||
println!("\n🎯 Summary");
|
||||
println!("=========");
|
||||
println!("Network Performance:");
|
||||
println!(" • Competitive with polymarket-rs-client baseline");
|
||||
println!(" • Network latency dominates end-to-end performance");
|
||||
println!(" • Geographic location affects results significantly");
|
||||
|
||||
|
||||
println!("\nComputational Performance:");
|
||||
println!(" • Order book operations: Sub-millisecond");
|
||||
println!(" • Fast calculations: Sub-microsecond");
|
||||
println!(" • JSON parsing: Microsecond-scale");
|
||||
println!(" • Memory efficient: Zero-allocation hot paths");
|
||||
|
||||
|
||||
println!("\n✨ polyfill-rs provides:");
|
||||
println!(" • Same network performance as alternatives");
|
||||
println!(" • Superior computational performance");
|
||||
println!(" • Memory-optimized data structures");
|
||||
println!(" • Fixed-point arithmetic advantages");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+34
-26
@@ -13,9 +13,9 @@ use polyfill_rs::{
|
||||
types::*,
|
||||
utils::time,
|
||||
};
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use rust_decimal_macros::dec;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{error, info, warn};
|
||||
@@ -92,7 +92,7 @@ impl SnipeStrategy {
|
||||
fill_engine: FillEngine::new(
|
||||
min_order_size,
|
||||
dec!(2.0), // 2% max slippage
|
||||
5, // 5 bps fee rate
|
||||
5, // 5 bps fee rate
|
||||
),
|
||||
stats: SnipeStats::default(),
|
||||
}
|
||||
@@ -105,16 +105,16 @@ impl SnipeStrategy {
|
||||
if data.token_id == self.token_id {
|
||||
self.process_book_update(data)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
StreamMessage::Trade { data } => {
|
||||
if data.token_id == self.token_id {
|
||||
self.process_trade(data)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
StreamMessage::Heartbeat { timestamp: _ } => {
|
||||
self.check_stale_quotes()?;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -123,13 +123,13 @@ impl SnipeStrategy {
|
||||
fn process_book_update(&mut self, delta: OrderDelta) -> Result<()> {
|
||||
// Ensure book exists
|
||||
self.book_manager.get_or_create_book(&self.token_id)?;
|
||||
|
||||
|
||||
// Update local order book
|
||||
self.book_manager.apply_delta(delta.clone())?;
|
||||
|
||||
// Get current book state
|
||||
let book = self.book_manager.get_book(&self.token_id)?;
|
||||
|
||||
|
||||
// Update best prices
|
||||
if let Some(best_bid) = book.bids.first() {
|
||||
self.last_best_bid = Some(best_bid.price);
|
||||
@@ -137,7 +137,7 @@ impl SnipeStrategy {
|
||||
if let Some(best_ask) = book.asks.first() {
|
||||
self.last_best_ask = Some(best_ask.price);
|
||||
}
|
||||
|
||||
|
||||
self.last_update = time::now_secs();
|
||||
|
||||
// Check for trading opportunities
|
||||
@@ -158,10 +158,10 @@ impl SnipeStrategy {
|
||||
|
||||
// Update statistics
|
||||
self.stats.total_volume += fill.size;
|
||||
|
||||
|
||||
// Calculate P&L if this was our trade
|
||||
// (In a real implementation, you'd track your own orders)
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -174,16 +174,14 @@ impl SnipeStrategy {
|
||||
|
||||
// Calculate spread
|
||||
let spread_pct = match (bid, ask) {
|
||||
(bid, ask) if bid > dec!(0) && ask > bid => {
|
||||
(ask - bid) / bid * dec!(100)
|
||||
}
|
||||
(bid, ask) if bid > dec!(0) && ask > bid => (ask - bid) / bid * dec!(100),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
// Check if spread is within our target
|
||||
if spread_pct <= self.max_spread_pct {
|
||||
self.stats.opportunities_detected += 1;
|
||||
|
||||
|
||||
info!(
|
||||
"Opportunity detected: spread {}% (target: {}%)",
|
||||
spread_pct, self.max_spread_pct
|
||||
@@ -200,8 +198,8 @@ impl SnipeStrategy {
|
||||
fn execute_snipe_order(&mut self, bid: Decimal, ask: Decimal) -> Result<()> {
|
||||
// Calculate order size (random between min and max)
|
||||
let random_factor = Decimal::from(rand::random::<u64>() % 100) / Decimal::from(100);
|
||||
let size = self.min_order_size +
|
||||
(self.max_order_size - self.min_order_size) * random_factor;
|
||||
let size =
|
||||
self.min_order_size + (self.max_order_size - self.min_order_size) * random_factor;
|
||||
|
||||
// Determine side based on market conditions
|
||||
let side = if bid > ask {
|
||||
@@ -222,7 +220,7 @@ impl SnipeStrategy {
|
||||
// Get current book for execution simulation
|
||||
let book = self.book_manager.get_book(&self.token_id)?;
|
||||
let mut book_impl = polyfill_rs::book::OrderBook::new(self.token_id.clone(), 100);
|
||||
|
||||
|
||||
// Convert to internal book format
|
||||
for level in &book.bids {
|
||||
book_impl.apply_delta(OrderDelta {
|
||||
@@ -234,7 +232,7 @@ impl SnipeStrategy {
|
||||
sequence: 1,
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
for level in &book.asks {
|
||||
book_impl.apply_delta(OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
@@ -248,7 +246,9 @@ impl SnipeStrategy {
|
||||
|
||||
// Execute order
|
||||
let start_time = std::time::Instant::now();
|
||||
let result = self.fill_engine.execute_market_order(&request, &book_impl)?;
|
||||
let result = self
|
||||
.fill_engine
|
||||
.execute_market_order(&request, &book_impl)?;
|
||||
let fill_time = start_time.elapsed().as_millis() as f64;
|
||||
|
||||
// Update statistics
|
||||
@@ -258,7 +258,8 @@ impl SnipeStrategy {
|
||||
}
|
||||
|
||||
// Update average fill time
|
||||
let total_time = self.stats.avg_fill_time_ms * (self.stats.orders_filled - 1) as f64 + fill_time;
|
||||
let total_time =
|
||||
self.stats.avg_fill_time_ms * (self.stats.orders_filled - 1) as f64 + fill_time;
|
||||
self.stats.avg_fill_time_ms = total_time / self.stats.orders_filled as f64;
|
||||
|
||||
info!(
|
||||
@@ -327,7 +328,11 @@ impl MockMarketData {
|
||||
let new_price = self.base_price * (Decimal::from(1) + price_change);
|
||||
|
||||
// Generate order book update
|
||||
let side = if rand::random::<bool>() { Side::BUY } else { Side::SELL };
|
||||
let side = if rand::random::<bool>() {
|
||||
Side::BUY
|
||||
} else {
|
||||
Side::SELL
|
||||
};
|
||||
let size = Decimal::from(rand::random::<u64>() % 1000 + 100);
|
||||
|
||||
StreamMessage::BookUpdate {
|
||||
@@ -338,7 +343,7 @@ impl MockMarketData {
|
||||
price: new_price,
|
||||
size,
|
||||
sequence: self.sequence,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +377,7 @@ async fn main() -> Result<()> {
|
||||
while message_count < max_messages {
|
||||
// Generate market update
|
||||
let update = market_data.generate_update();
|
||||
|
||||
|
||||
// Process update
|
||||
if let Err(e) = strategy.process_update(update) {
|
||||
error!("Error processing update: {}", e);
|
||||
@@ -397,7 +402,10 @@ async fn main() -> Result<()> {
|
||||
// Print final statistics
|
||||
let final_stats = strategy.get_stats();
|
||||
info!("Final statistics:");
|
||||
info!(" Opportunities detected: {}", final_stats.opportunities_detected);
|
||||
info!(
|
||||
" Opportunities detected: {}",
|
||||
final_stats.opportunities_detected
|
||||
);
|
||||
info!(" Orders placed: {}", final_stats.orders_placed);
|
||||
info!(" Orders filled: {}", final_stats.orders_filled);
|
||||
info!(" Total volume: {}", final_stats.total_volume);
|
||||
@@ -405,4 +413,4 @@ async fn main() -> Result<()> {
|
||||
|
||||
info!("Snipe trading example completed!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user