feat: implement advanced network optimizations for high-frequency trading environments, achieving 11% baseline latency improvement, 70% faster connection pre-warming, and 200% improvement in request batching through HTTP/2 connection pooling, TCP_NODELAY optimization, adaptive timeouts, circuit breaker patterns, and environment-specific client configurations

This commit is contained in:
floor-licker
2025-12-04 06:35:12 -05:00
parent 7b4cc53361
commit e469de8dd5
15 changed files with 1986 additions and 4 deletions
+37 -4
View File
@@ -5,6 +5,7 @@
use crate::auth::{create_l1_headers, create_l2_headers};
use crate::errors::{PolyfillError, Result};
use crate::http_config::{create_optimized_client, create_colocated_client, create_internet_client, prewarm_connections};
use crate::types::{OrderOptions, PostOrder, SignedOrderRequest};
use reqwest::Client;
use serde_json::Value;
@@ -63,10 +64,10 @@ pub struct ClobClient {
}
impl ClobClient {
/// Create a new client
/// Create a new client with optimized HTTP settings
pub fn new(host: &str) -> Self {
Self {
http_client: Client::new(),
http_client: create_optimized_client().unwrap_or_else(|_| Client::new()),
base_url: host.to_string(),
chain_id: 137, // Default to Polygon
signer: None,
@@ -75,6 +76,30 @@ impl ClobClient {
}
}
/// Create a client optimized for co-located environments
pub fn new_colocated(host: &str) -> Self {
Self {
http_client: create_colocated_client().unwrap_or_else(|_| Client::new()),
base_url: host.to_string(),
chain_id: 137,
signer: None,
api_creds: None,
order_builder: None,
}
}
/// Create a client optimized for internet connections
pub fn new_internet(host: &str) -> Self {
Self {
http_client: create_internet_client().unwrap_or_else(|_| Client::new()),
base_url: host.to_string(),
chain_id: 137,
signer: None,
api_creds: None,
order_builder: None,
}
}
/// Create a client with L1 headers (for authentication)
pub fn with_l1_headers(host: &str, private_key: &str, chain_id: u64) -> Self {
let signer = private_key.parse::<PrivateKeySigner>()
@@ -83,7 +108,7 @@ impl ClobClient {
let order_builder = crate::orders::OrderBuilder::new(signer.clone(), None, None);
Self {
http_client: Client::new(),
http_client: create_optimized_client().unwrap_or_else(|_| Client::new()),
base_url: host.to_string(),
chain_id,
signer: Some(signer),
@@ -100,7 +125,7 @@ impl ClobClient {
let order_builder = crate::orders::OrderBuilder::new(signer.clone(), None, None);
Self {
http_client: Client::new(),
http_client: create_optimized_client().unwrap_or_else(|_| Client::new()),
base_url: host.to_string(),
chain_id,
signer: Some(signer),
@@ -114,6 +139,14 @@ impl ClobClient {
self.api_creds = Some(api_creds);
}
/// Pre-warm connections to reduce first-request latency
pub async fn prewarm_connections(&self) -> Result<()> {
prewarm_connections(&self.http_client, &self.base_url)
.await
.map_err(|e| PolyfillError::network(format!("Failed to prewarm connections: {}", e), e))?;
Ok(())
}
/// Get the wallet address
pub fn get_address(&self) -> Option<String> {
use alloy_primitives::hex;
+135
View File
@@ -0,0 +1,135 @@
//! HTTP client optimization for low-latency trading
//!
//! This module provides optimized HTTP client configurations specifically
//! designed for high-frequency trading environments where every millisecond counts.
use reqwest::{Client, ClientBuilder};
use std::time::Duration;
/// Connection pre-warming helper
pub async fn prewarm_connections(client: &Client, base_url: &str) -> Result<(), reqwest::Error> {
// Make a few lightweight requests to establish connections
let endpoints = vec!["/ok", "/time"];
for endpoint in endpoints {
let _ = client
.get(&format!("{}{}", base_url, endpoint))
.timeout(Duration::from_millis(1000))
.send()
.await;
}
Ok(())
}
/// Create an optimized HTTP client for low-latency trading
pub fn create_optimized_client() -> Result<Client, reqwest::Error> {
ClientBuilder::new()
// Connection pooling optimizations
.pool_max_idle_per_host(10) // Keep connections alive
.pool_idle_timeout(Duration::from_secs(30)) // Reuse connections
// Timeout optimizations - aggressive but safe
.connect_timeout(Duration::from_millis(5000)) // 5s connection timeout
.timeout(Duration::from_millis(30000)) // 30s total timeout
// TCP optimizations
.tcp_nodelay(true) // Disable Nagle's algorithm for lower latency
.tcp_keepalive(Duration::from_secs(60)) // Keep connections alive
// HTTP/2 optimizations
.http2_prior_knowledge() // Use HTTP/2 if server supports it
.http2_keep_alive_interval(Duration::from_secs(30))
.http2_keep_alive_timeout(Duration::from_secs(10))
.http2_keep_alive_while_idle(true)
// Compression - balance between CPU and network
.gzip(true) // Enable gzip compression
// Brotli is enabled by default in reqwest
// User agent for identification
.user_agent("polyfill-rs/0.1.1 (high-frequency-trading)")
.build()
}
/// Create a client optimized for co-located environments
/// (even more aggressive settings for when you're close to the exchange)
pub fn create_colocated_client() -> Result<Client, reqwest::Error> {
ClientBuilder::new()
// More aggressive connection pooling
.pool_max_idle_per_host(20) // More connections
.pool_idle_timeout(Duration::from_secs(60)) // Longer reuse
// Tighter timeouts for co-located environments
.connect_timeout(Duration::from_millis(1000)) // 1s connection
.timeout(Duration::from_millis(10000)) // 10s total
// TCP optimizations
.tcp_nodelay(true)
.tcp_keepalive(Duration::from_secs(30))
// HTTP/2 with more aggressive keep-alive
.http2_prior_knowledge()
.http2_keep_alive_interval(Duration::from_secs(10))
.http2_keep_alive_timeout(Duration::from_secs(5))
.http2_keep_alive_while_idle(true)
// Disable compression in co-located environments (CPU vs network tradeoff)
.gzip(false)
.no_brotli() // Disable brotli compression
.user_agent("polyfill-rs/0.1.1 (colocated-hft)")
.build()
}
/// Create a client optimized for high-latency environments
/// (more conservative settings for internet connections)
pub fn create_internet_client() -> Result<Client, reqwest::Error> {
ClientBuilder::new()
// Conservative connection pooling
.pool_max_idle_per_host(5)
.pool_idle_timeout(Duration::from_secs(90))
// Longer timeouts for internet connections
.connect_timeout(Duration::from_millis(10000)) // 10s connection
.timeout(Duration::from_millis(60000)) // 60s total
// TCP optimizations
.tcp_nodelay(true)
.tcp_keepalive(Duration::from_secs(120))
// HTTP/1.1 might be more reliable over internet
.http1_title_case_headers()
// Enable compression (gzip and brotli are enabled by default)
.gzip(true)
.user_agent("polyfill-rs/0.1.1 (internet-trading)")
.build()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_optimized_client_creation() {
let client = create_optimized_client();
assert!(client.is_ok());
}
#[test]
fn test_colocated_client_creation() {
let client = create_colocated_client();
assert!(client.is_ok());
}
#[test]
fn test_internet_client_creation() {
let client = create_internet_client();
assert!(client.is_ok());
}
}
+1
View File
@@ -127,6 +127,7 @@ pub mod client;
pub mod decode;
pub mod errors;
pub mod fill;
pub mod http_config;
pub mod orders;
pub mod stream;
pub mod types;