The Rust SDK is currently in beta. Visit github.com/kuestcom for the latest version.
Installation
Add to yourCargo.toml:
Cargo.toml
[dependencies]
kuest-sdk = "0.1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Quick start
use kuest_sdk::{KuestClient, OrderSide, OrderType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize client
let client = KuestClient::new(
"your-api-key",
"your-api-secret",
"your-passphrase",
true, // testnet
)?;
// Get markets
let markets = client.get_markets().await?;
println!("Found {} markets", markets.len());
// Place order
let order = client
.place_order(
"0x123...",
OrderSide::Buy,
OrderType::Limit,
0.65,
10.0,
)
.await?;
println!("Order placed: {}", order.id);
Ok(())
}
Core features
Market data
use kuest_sdk::MarketStatus;
// Get active markets
let markets = client
.get_markets()
.status(MarketStatus::Active)
.await?;
for market in markets {
println!("{}: {:.2}%", market.title, market.yes_price * 100.0);
}
// Get specific market
let market = client.get_market("0x123...").await?;
println!("Volume: ${:.2}", market.volume);
// Get orderbook
let orderbook = client.get_orderbook("0x123...").await?;
println!("Spread: {:.4}", orderbook.spread());
Place orders
use kuest_sdk::{OrderSide, OrderType, Outcome};
// Limit order
let order = client
.place_order(
"0x123...",
OrderSide::Buy,
OrderType::Limit,
0.65,
10.0,
)
.outcome(Outcome::Yes)
.await?;
// Market order
let order = client
.place_order(
"0x123...",
OrderSide::Sell,
OrderType::Market,
0.0, // price ignored for market orders
5.0,
)
.outcome(Outcome::No)
.await?;
Manage orders
// Get open orders
let orders = client
.get_orders()
.market_id("0x123...")
.await?;
for order in orders {
println!("{:?} {} @ {}", order.side, order.size, order.price);
}
// Cancel order
client.cancel_order(&order_id).await?;
// Cancel all orders
client.cancel_all_orders("0x123...").await?;
Portfolio
// Get positions
let positions = client.get_positions().await?;
for position in positions {
println!(
"{}: {} @ {} (PnL: ${:.2})",
position.market,
position.size,
position.avg_price,
position.pnl
);
}
// Get trade history
let trades = client.get_trades().limit(100).await?;
Advanced features
WebSocket streaming
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (tx, mut rx) = mpsc::unbounded_channel();
// Subscribe to price updates
tokio::spawn(async move {
client
.subscribe_prices(vec!["0x123...", "0x456..."], tx)
.await
.unwrap();
});
// Process price updates
while let Some(update) = rx.recv().await {
println!("{}: {}", update.market_id, update.price);
}
Ok(())
}
Concurrent operations
use tokio::try_join;
// Fetch multiple markets concurrently
let (market1, market2, market3) = try_join!(
client.get_market("0x123..."),
client.get_market("0x456..."),
client.get_market("0x789..."),
)?;
println!("Fetched 3 markets concurrently");
Error handling
use kuest_sdk::Error;
match client.place_order("0x123...", OrderSide::Buy, OrderType::Limit, 0.65, 1000.0).await {
Ok(order) => println!("Order placed: {}", order.id),
Err(Error::InsufficientBalance) => println!("Not enough USDC"),
Err(Error::RateLimit { retry_after }) => {
println!("Rate limited. Retry after {} seconds", retry_after);
tokio::time::sleep(tokio::time::Duration::from_secs(retry_after)).await;
}
Err(e) => println!("Error: {}", e),
}
Custom retry logic
use tokio::time::{sleep, Duration};
async fn retry<F, T, E>(
mut f: F,
max_retries: u32,
) -> Result<T, E>
where
F: FnMut() -> Result<T, E>,
{
let mut retries = 0;
loop {
match f() {
Ok(val) => return Ok(val),
Err(e) if retries >= max_retries => return Err(e),
Err(_) => {
retries += 1;
sleep(Duration::from_millis(2u64.pow(retries) * 100)).await;
}
}
}
}
Example strategies
Market maker
use tokio::time::{interval, Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = KuestClient::new(/* ... */).unwrap();
let mut interval = interval(Duration::from_secs(60));
loop {
interval.tick().await;
if let Err(e) = market_make(&client, "0x123...", 0.02, 5.0).await {
eprintln!("Market making error: {}", e);
}
}
}
async fn market_make(
client: &KuestClient,
market_id: &str,
spread: f64,
size: f64,
) -> Result<(), Box<dyn std::error::Error>> {
let orderbook = client.get_orderbook(market_id).await?;
let mid_price = (orderbook.best_bid + orderbook.best_ask) / 2.0;
// Place buy order
client
.place_order(
market_id,
OrderSide::Buy,
OrderType::Limit,
mid_price - spread / 2.0,
size,
)
.await?;
// Place sell order
client
.place_order(
market_id,
OrderSide::Sell,
OrderType::Limit,
mid_price + spread / 2.0,
size,
)
.await?;
println!("Market making at {:.4} +/- {:.4}", mid_price, spread / 2.0);
Ok(())
}
High-frequency arbitrage
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Arc::new(KuestClient::new(/* ... */).unwrap());
let markets = client.get_markets().await?;
// Check all markets concurrently
let mut tasks = vec![];
for market in markets {
let client = Arc::clone(&client);
tasks.push(tokio::spawn(async move {
check_arbitrage(&client, &market.id).await
}));
}
// Wait for all checks to complete
for task in tasks {
task.await??;
}
Ok(())
}
async fn check_arbitrage(
client: &KuestClient,
market_id: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let orderbook = client.get_orderbook(market_id).await?;
let yes_ask = orderbook.asks.first().map(|o| o.price).unwrap_or(1.0);
let no_ask = orderbook.asks.first().map(|o| o.price).unwrap_or(1.0);
if yes_ask + no_ask < 0.98 {
println!("Arbitrage opportunity in {}", market_id);
println!(" Profit: {:.4} USDC", 1.0 - (yes_ask + no_ask));
// Execute arbitrage
tokio::try_join!(
client.place_order(market_id, OrderSide::Buy, OrderType::Limit, yes_ask, 1.0),
client.place_order(market_id, OrderSide::Buy, OrderType::Limit, no_ask, 1.0),
)?;
}
Ok(())
}
Performance optimizations
Connection pooling
use kuest_sdk::ClientBuilder;
let client = ClientBuilder::new()
.api_key("your-api-key")
.api_secret("your-api-secret")
.passphrase("your-passphrase")
.pool_max_idle_per_host(10)
.timeout(Duration::from_secs(5))
.build()?;
Zero-copy deserialization
use serde::{Deserialize, Serialize};
use zerocopy::AsBytes;
#[derive(Deserialize, AsBytes)]
struct OrderbookUpdate {
market_id: [u8; 32],
price: f64,
size: f64,
timestamp: u64,
}
Batch operations
// Place multiple orders in one request
let orders = vec![
OrderRequest::new("0x123...", OrderSide::Buy, 0.60, 5.0),
OrderRequest::new("0x456...", OrderSide::Sell, 0.40, 10.0),
OrderRequest::new("0x789...", OrderSide::Buy, 0.75, 3.0),
];
let results = client.place_orders_batch(orders).await?;
println!("Placed {} orders", results.len());
Benchmarks
Rust SDK benchmarks on AWS c5.large:
- Order placement: ~2ms latency
- Orderbook fetch: ~5ms latency
- WebSocket updates: ~0.5ms processing
- Concurrent requests: 10,000 req/s
Best practices
Use async/await
Leverage Tokio for non-blocking I/O:
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {
// Your bot logic
}
Implement graceful shutdown
use tokio::signal;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tokio::select! {
_ = run_bot() => {},
_ = signal::ctrl_c() => {
println!("Shutting down...");
// Cleanup logic
}
}
Ok(())
}
Next steps
Python SDK
Python SDK documentation
Arbitrage
Advanced arbitrage strategies
API reference
Complete API documentation
Bot SDKs
SDK overview