Skip to main content
The Kuest Rust SDK provides a low-latency, type-safe interface for building high-frequency trading strategies.
The Rust SDK is currently in beta. Visit github.com/kuestcom for the latest version.

Installation

Add to your Cargo.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

1

Use async/await

Leverage Tokio for non-blocking I/O:
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {
    // Your bot logic
}
2

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(())
}
3

Log metrics

use tracing::{info, error};

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    info!("Bot started");
    match place_order().await {
        Ok(_) => info!("Order placed successfully"),
        Err(e) => error!("Order failed: {}", e),
    }
}

Next steps

Python SDK

Python SDK documentation

Arbitrage

Advanced arbitrage strategies

API reference

Complete API documentation

Bot SDKs

SDK overview

Build docs developers (and LLMs) love