Rust Examples
Order Router
Order Router — CCXT Rust code example.
// OrderRouter — ask the router how to convert one asset into another.
//
// The router holds live L2 books from many venues and answers "what is the
// cheapest way to turn X into Y right now, and on which venues" — book-walked
// to your actual size, fee-adjusted, and split across venues when that beats
// any single one.
//
// This example is READ-ONLY: it asks for a recommendation and prints it. It
// never places an order. Execution lives behind router.execute(plan, venues),
// which defaults to dry_run and refuses to trade unless explicitly told to.
//
// Usage:
// cargo run --manifest-path examples/rust/Cargo.toml --bin order_router
//
// The router service is public: no API key, no signup.
use ccxt::value::HashMap;
use ccxt::{OrderRouter, Value};
#[tokio::main]
async fn main() {
let config = HashMap::new();
// config.insert("baseUrl".to_string(), Value::Str("https://docs.ccxt.com/router/api".into())); // the default
let router = match OrderRouter::new(&Value::Map(config)) {
Ok(client) => client,
Err(e) => {
eprintln!("{e}");
return;
}
};
// Exactly one of amountIn or amountOut — never both, and never neither.
// They are different book traversals, not a unit conversion: amountIn walks
// until the money runs out, amountOut until the size is reached.
let mut params = HashMap::new();
params.insert("amountIn".to_string(), Value::Float(20.0));
params.insert("strategy".to_string(), Value::Str("split_optimal".to_string()));
let route = match router.fetch_route("USDT", "BTC", &Value::Map(params)).await {
Ok(found) => found,
Err(e) => {
eprintln!("{e}");
return;
}
};
// An unroutable pair comes back as a result with a reason, NOT an error.
// Refusing to quote is a deliberate outcome, not a failure.
let unroutable = router.string_at(&route, "unroutableReason", "");
if !unroutable.is_empty() {
println!("unroutable: {unroutable}");
return;
}
println!(
"{} {} -> {} {}",
router.number_at(&route, "amountIn", 0.0),
router.string_at(&route, "from", ""),
router.number_at(&route, "amountOut", 0.0),
router.string_at(&route, "to", ""),
);
println!("effective rate {}", router.number_at(&route, "effectiveRate", 0.0));
// Positive impact is worse.
println!("price impact {} bps", router.number_at(&route, "impactBps", 0.0));
println!("fill ratio {}", router.number_at(&route, "fillRatio", 0.0));
// One hop is a direct conversion; more than one means it was bridged
// (e.g. SOL -> USDT -> BTC), and each hop is a separate order.
for (index, hop) in router.list_at(&route, "hops").iter().enumerate() {
let legs = router.list_at(hop, "legs");
println!(
"hop {} {} {} - {} venue(s)",
index + 1,
router.string_at(hop, "pair", ""),
router.string_at(hop, "side", ""),
legs.len(),
);
for leg in &legs {
println!(
" {} {} @ {}",
router.string_at(leg, "exchangeId", ""),
router.number_at(leg, "amount", 0.0),
router.number_at(leg, "averagePrice", 0.0),
);
}
}
}