//! Demo for polyfill-rs //! //! This example demonstrates all the major functions and capabilities of the polyfill-rs library: //! - Basic client operations and API calls //! - Order book management and analytics //! - Real-time streaming capabilities //! - Trade execution and fill processing //! - Utility functions and mathematical operations //! - Error handling and retry logic //! - Rate limiting and performance optimizations use polyfill_rs::{ // Order book management book::{OrderBook, OrderBookManager}, // Error handling errors::{PolyfillError, Result}, // Fill execution fill::{FillEngine, FillProcessor}, // Streaming capabilities stream::{StreamManager, WebSocketStream}, // Types and structures types::*, // Utility functions utils::{address, 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}; /// Demo showcasing polyfill-rs functionality #[allow(dead_code)] pub struct PolyfillDemo { /// Basic HTTP client client: ClobClient, /// Advanced client with configuration advanced_client: PolyfillClient, /// Order book manager book_manager: OrderBookManager, /// Fill engine for trade execution fill_engine: FillEngine, /// Fill processor for handling fills fill_processor: FillProcessor, /// Stream manager for real-time data stream_manager: StreamManager, /// Rate limiter rate_limiter: rate_limit::TokenBucket, /// Statistics stats: DemoStats, } /// Demo statistics #[derive(Debug, Clone)] pub struct DemoStats { pub api_calls: u64, pub orders_processed: u64, pub fills_processed: u64, pub stream_messages: u64, pub errors: u64, pub total_volume: Decimal, } impl Default for DemoStats { fn default() -> Self { Self { api_calls: 0, orders_processed: 0, fills_processed: 0, stream_messages: 0, errors: 0, total_volume: dec!(0), } } } impl PolyfillDemo { /// Create a new demo pub fn new() -> Result { // Create basic client let config = ClientConfig { base_url: "https://clob.polymarket.com".to_string(), chain: 137, private_key: None, api_credentials: None, builder_code: None, signature_type: None, funder: None, timeout: Some(Duration::from_secs(30)), max_connections: Some(100), }; let client = ClobClient::new(&config.base_url); // Create advanced client with configuration let advanced_client = PolyfillClient::from_config(config.clone())?; // 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 ); // 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, book_manager, fill_engine, fill_processor, stream_manager, rate_limiter, stats: DemoStats::default(), }) } /// 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) => { info!("Found {} markets", markets.data.len()); for market in &markets.data[..std::cmp::min(3, markets.data.len())] { 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() ); // 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 { token_id: token_id.to_string(), timestamp: chrono::Utc::now(), side: Side::BUY, price: bid.price, size: bid.size, sequence: i as u64, })?; } for (i, ask) in order_book.asks.iter().enumerate() { local_book.apply_delta(OrderDelta { token_id: token_id.to_string(), timestamp: chrono::Utc::now(), side: Side::SELL, price: ask.price, size: ask.size, 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 ); if let Some(spread) = analytics.spread { 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:"); info!(" Average price: {}", impact.average_price); 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); info!("Created order args: {:?}", order_args); // Create market order request let market_order = MarketOrderRequest { token_id: "12345".to_string(), side: Side::BUY, amount: dec!(100.0), 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(), side: Side::BUY, price: dec!(0.75), size: dec!(100.0), order_type: OrderType::GTC, 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 { token_id: "12345".to_string(), timestamp: chrono::Utc::now(), side: Side::BUY, price: dec!(0.70) + Decimal::from(i) * dec!(0.01), size: dec!(100.0), sequence: i, })?; } for i in 1..=5 { book.apply_delta(OrderDelta { token_id: "12345".to_string(), timestamp: chrono::Utc::now(), side: Side::SELL, price: dec!(0.80) + Decimal::from(i) * dec!(0.01), size: dec!(100.0), sequence: i + 10, })?; } info!("Created order book with liquidity"); // Execute market order let market_order = MarketOrderRequest { token_id: "12345".to_string(), side: Side::BUY, amount: dec!(50.0), 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)?; info!("Market order execution result:"); info!(" Status: {:?}", fill_result.status); info!(" Total size: {}", fill_result.total_size); info!(" Average price: {}", fill_result.average_price); 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(), side: Side::SELL, price: dec!(0.85), size: dec!(25.0), order_type: OrderType::GTC, 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 ); 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"; match address::parse_address(address) { 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" } ); } 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, initial_delay: Duration::from_millis(100), max_delay: Duration::from_secs(1), backoff_factor: 2.0, jitter: true, }; let operation = || async { // Simulate a potentially failing operation if rand::random::() { Ok("Success!") } else { 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"), ); 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://ws-subscriptions-clob.polymarket.com/ws/market"); info!("Created WebSocket stream"); // Simulate subscription let subscription = WssSubscription { channel_type: "user".to_string(), operation: Some("subscribe".to_string()), markets: vec!["market1".to_string(), "market2".to_string()], asset_ids: vec!["12345".to_string(), "67890".to_string()], initial_dump: Some(true), custom_feature_enabled: None, auth: Some(WssAuth { api_key: "test-api-key".to_string(), secret: "test-secret".to_string(), passphrase: "test-passphrase".to_string(), }), }; info!("Created subscription: {:?}", subscription); // Simulate receiving stream messages let messages = vec![ StreamMessage::Book(BookUpdate { asset_id: "12345".to_string(), market: "market1".to_string(), timestamp: time::now_millis(), bids: vec![OrderSummary { price: dec!(0.75), size: dec!(100.0), }], asks: vec![OrderSummary { price: dec!(0.76), size: dec!(50.0), }], hash: None, }), StreamMessage::Trade(TradeMessage { id: "fill1".to_string(), market: "market1".to_string(), asset_id: "12345".to_string(), side: Side::BUY, size: dec!(50.0), price: dec!(0.75), status: Some("MATCHED".to_string()), msg_type: None, last_update: None, matchtime: None, timestamp: None, }), ]; for message in messages { info!("Received stream message: {:?}", message); self.stats.stream_messages += 1; // Process message based on type match &message { StreamMessage::Book(book) => { info!(" Processing book update for asset: {}", book.asset_id); // This is a demo: apply snapshot levels as deltas. for level in &book.bids { let _ = self.book_manager.apply_delta(OrderDelta { token_id: book.asset_id.clone(), timestamp: chrono::Utc::now(), side: Side::BUY, price: level.price, size: level.size, sequence: book.timestamp, }); } for level in &book.asks { let _ = self.book_manager.apply_delta(OrderDelta { token_id: book.asset_id.clone(), timestamp: chrono::Utc::now(), side: Side::SELL, price: level.price, size: level.size, sequence: book.timestamp, }); } }, StreamMessage::Trade(trade) => { info!( " Processing trade: {} {} @ {}", trade.side.as_str(), trade.size, trade.price ); }, _ => { 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:"); info!(" Total orders: {}", fill_stats.total_orders); 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:"); info!(" Pending orders: {}", processor_stats.pending_orders); info!(" Pending fills: {}", processor_stats.pending_fills); 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); info!(" Orders processed: {}", self.stats.orders_processed); info!(" Fills processed: {}", self.stats.fills_processed); 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 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 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!("Demo completed successfully!"); Ok(()) } } #[tokio::main] async fn main() -> Result<()> { // Initialize logging tracing_subscriber::fmt::init(); info!("Polyfill-rs 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(()) }