Rust Examples
Ob Live Bench
Ob Live Bench — CCXT Rust code example.
// Live watch_order_book CPU benchmark — CCXT side of L1nkus's comparison
// (PR #28627). Streams binance USD-M futures BTC/USDT:USDT @depth and prints,
// every 5s: frames processed, current book depth (bids/asks), 5s CPU%, and
// cumulative CPU — mirroring the report's format so before/after is directly
// comparable.
//
// cargo run --release --features ws --bin ob_live_bench -- [run_secs]
use ccxt::{Config, Params};
use ccxt_pro::{from_id_with_config, TypedExchange, TypedExchangeExt};
use std::time::{Duration, Instant};
fn cpu_secs() -> f64 {
std::fs::read_to_string("/proc/self/stat")
.ok()
.and_then(|s| {
let close = s.rfind(')')?;
let rest: Vec<&str> = s[close + 2..].split_whitespace().collect();
let utime: f64 = rest.get(11)?.parse().ok()?;
let stime: f64 = rest.get(12)?.parse().ok()?;
Some((utime + stime) / 100.0)
})
.unwrap_or(0.0)
}
async fn run() {
let sym = std::env::var("CCXT_SYMBOL").unwrap_or_else(|_| "BTC/USDT:USDT".to_string());
let run_secs: u64 = std::env::args().nth(1).and_then(|s| s.parse().ok()).unwrap_or(30);
let id = std::env::var("CCXT_EXCHANGE").unwrap_or_else(|_| "binance".to_string());
// linear-only so the "BTCUSDT" WS id resolves to the perpetual, not spot.
let cfg = Config::new().option("fetchMarkets", Params::new().with_strs("types", &["linear"]));
let mut ex: Box<dyn TypedExchange> = from_id_with_config(&id, cfg).expect("no typed WS wrapper");
ex.load_markets(false).await;
println!("Streaming {id} {sym} watch_order_book for {run_secs}s…");
let start = Instant::now();
let cpu_start = cpu_secs();
let mut tick_frames: u64 = 0;
let mut last_tick = Instant::now();
let mut last_cpu = cpu_secs();
loop {
if start.elapsed().as_secs() >= run_secs {
break;
}
match ex.watch_order_book(&sym, None, Params::none()).await {
Ok(ob) => {
tick_frames += 1;
if last_tick.elapsed() >= Duration::from_secs(5) {
let cpu_now = cpu_secs();
let cpu5 = cpu_now - last_cpu;
println!(
"Tick | Frames (5s): {:<5} | Bids: {:<5} Asks: {:<5} | 5s CPU%: {:>5.1}% | Total CPU: {:.2}s",
tick_frames,
ob.bids.len(),
ob.asks.len(),
cpu5 / 5.0 * 100.0,
cpu_now - cpu_start,
);
tick_frames = 0;
last_tick = Instant::now();
last_cpu = cpu_now;
}
}
Err(e) => {
eprintln!("watch error: {e}");
tokio::time::sleep(Duration::from_millis(500)).await;
}
}
}
}
fn main() {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.thread_stack_size(64 * 1024 * 1024)
.enable_all()
.build()
.unwrap();
rt.block_on(run());
}