Rust SDK

Order Operations

Placing, canceling, and managing orders with the Rust SDK - builder patterns, batch operations, and error handling

Order Operations

The Rust SDK provides type-safe order management with builder patterns, batch operations, and comprehensive error handling.

Order Types

use lux_dex_sdk::{Order, OrderType, Side, TimeInForce};

// Limit order
let limit = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Buy)
    .price(50_000.0)
    .size(1.0)
    .time_in_force(TimeInForce::Gtc)
    .build()?;

// Market order
let market = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Market)
    .side(Side::Sell)
    .size(0.5)
    .build()?;

// Stop-limit order
let stop_limit = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::StopLimit)
    .side(Side::Sell)
    .stop_price(48_000.0)
    .price(47_900.0)
    .size(1.0)
    .build()?;

// Iceberg order
let iceberg = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Iceberg)
    .side(Side::Buy)
    .price(50_000.0)
    .size(10.0)
    .display_size(1.0)  // Show only 1.0 at a time
    .build()?;

Place Order

Basic Order Placement

use lux_dex_sdk::{Client, Order, OrderType, Side, Result};

async fn place_limit_order(client: &Client) -> Result<OrderId> {
    let order = Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Buy)
        .price(50_000.0)
        .size(1.0)
        .build()?;

    let response = client.place_order(order).await?;

    println!("Order ID: {}", response.id);
    println!("Status: {:?}", response.status);
    println!("Filled: {} / {}", response.filled_size, response.size);

    Ok(response.id)
}

With Client Order ID

use uuid::Uuid;

let client_order_id = Uuid::new_v4().to_string();

let order = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Buy)
    .price(50_000.0)
    .size(1.0)
    .client_order_id(&client_order_id)
    .build()?;

let response = client.place_order(order).await?;
assert_eq!(response.client_order_id, Some(client_order_id));

Post-Only Orders

// Post-only: order rejected if it would immediately match
let order = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Buy)
    .price(50_000.0)
    .size(1.0)
    .post_only(true)
    .build()?;

match client.place_order(order).await {
    Ok(response) => println!("Order placed: {}", response.id),
    Err(Error::WouldImmediatelyMatch) => {
        println!("Order would cross spread, rejected");
    }
    Err(e) => return Err(e),
}

Reduce-Only Orders

// Reduce-only: can only reduce existing position
let order = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Sell)
    .price(55_000.0)
    .size(0.5)
    .reduce_only(true)
    .build()?;

Cancel Order

By Order ID

async fn cancel_order(client: &Client, order_id: OrderId) -> Result<()> {
    let response = client.cancel_order(order_id).await?;

    match response.status {
        CancelStatus::Cancelled => println!("Order cancelled"),
        CancelStatus::NotFound => println!("Order not found"),
        CancelStatus::AlreadyFilled => println!("Order already filled"),
        CancelStatus::AlreadyCancelled => println!("Order already cancelled"),
    }

    Ok(())
}

By Client Order ID

let response = client
    .cancel_order_by_client_id("my-unique-id")
    .await?;

Cancel All Orders

// Cancel all orders for a symbol
let cancelled = client.cancel_all_orders("BTC-USD").await?;
println!("Cancelled {} orders", cancelled.len());

// Cancel all orders
let cancelled = client.cancel_all_orders_global().await?;
println!("Cancelled {} orders across all symbols", cancelled.len());

Modify Order

Replace Order

// Atomic replace: cancel + place in single request
let new_order = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Buy)
    .price(49_500.0)  // New price
    .size(1.5)        // New size
    .build()?;

let response = client.replace_order(old_order_id, new_order).await?;
println!("New order ID: {}", response.id);

Amend Order (Price/Size Only)

// Faster than replace - keeps same order ID
let amendment = OrderAmendment {
    order_id,
    new_price: Some(49_500.0),
    new_size: Some(1.5),
};

let response = client.amend_order(amendment).await?;

Query Orders

Get Order by ID

let order = client.get_order(order_id).await?;

println!("Order: {:?}", order);
println!("Status: {:?}", order.status);
println!("Filled: {} / {}", order.filled_size, order.size);
println!("Avg fill price: {:?}", order.avg_fill_price);

Get Open Orders

// Get all open orders
let orders = client.get_open_orders().await?;

// Get open orders for symbol
let orders = client.get_open_orders_by_symbol("BTC-USD").await?;

for order in orders {
    println!("{}: {} {} @ {}",
        order.id,
        order.side,
        order.size,
        order.price.unwrap_or_default()
    );
}

Get Order History

use lux_dex_sdk::OrderFilter;

let filter = OrderFilter {
    symbol: Some("BTC-USD".into()),
    side: Some(Side::Buy),
    start_time: Some(Utc::now() - Duration::days(7)),
    end_time: Some(Utc::now()),
    limit: Some(100),
    ..Default::default()
};

let orders = client.get_order_history(filter).await?;

Batch Operations

Batch Place Orders

use lux_dex_sdk::BatchOrder;

let orders = vec![
    Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Buy)
        .price(49_000.0)
        .size(1.0)
        .build()?,
    Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Buy)
        .price(48_000.0)
        .size(1.0)
        .build()?,
    Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Sell)
        .price(52_000.0)
        .size(1.0)
        .build()?,
];

let results = client.place_orders_batch(orders).await?;

for result in results {
    match result {
        Ok(response) => println!("Placed: {}", response.id),
        Err(e) => println!("Failed: {}", e),
    }
}

Batch Cancel Orders

let order_ids = vec![order_id_1, order_id_2, order_id_3];
let results = client.cancel_orders_batch(order_ids).await?;

let cancelled_count = results.iter().filter(|r| r.is_ok()).count();
println!("Cancelled {} of {} orders", cancelled_count, results.len());

Order Builder Details

Complete Builder API

let order = Order::builder()
    // Required fields
    .symbol("BTC-USD")
    .side(Side::Buy)
    .size(1.0)

    // Order type (default: Limit)
    .order_type(OrderType::Limit)

    // Price fields
    .price(50_000.0)
    .stop_price(49_000.0)  // For stop orders

    // Time in force (default: GTC)
    .time_in_force(TimeInForce::Gtc)

    // Optional fields
    .client_order_id("my-id")
    .display_size(0.5)      // For iceberg
    .post_only(false)
    .reduce_only(false)

    // Expiration (for GTD orders)
    .expire_time(Utc::now() + Duration::hours(24))

    // Build with validation
    .build()?;

Validation

use lux_dex_sdk::{Order, ValidationError};

let result = Order::builder()
    .symbol("BTC-USD")
    .order_type(OrderType::Limit)
    .side(Side::Buy)
    // Missing price for limit order!
    .size(1.0)
    .build();

match result {
    Err(ValidationError::MissingPrice) => {
        println!("Limit orders require a price");
    }
    _ => {}
}

Order Response Types

/// Response from placing an order
#[derive(Debug, Clone)]
pub struct OrderResponse {
    pub id: OrderId,
    pub client_order_id: Option<String>,
    pub symbol: String,
    pub order_type: OrderType,
    pub side: Side,
    pub price: Option<f64>,
    pub size: f64,
    pub filled_size: f64,
    pub remaining_size: f64,
    pub avg_fill_price: Option<f64>,
    pub status: OrderStatus,
    pub time_in_force: TimeInForce,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub fills: Vec<Fill>,
}

/// Individual fill
#[derive(Debug, Clone)]
pub struct Fill {
    pub trade_id: TradeId,
    pub price: f64,
    pub size: f64,
    pub fee: f64,
    pub fee_currency: String,
    pub timestamp: DateTime<Utc>,
}

Error Handling

use lux_dex_sdk::{Error, OrderError};

async fn place_order_safe(client: &Client, order: Order) -> Result<OrderResponse> {
    match client.place_order(order).await {
        Ok(response) => Ok(response),

        // Order-specific errors
        Err(Error::Order(OrderError::InsufficientBalance { required, available })) => {
            eprintln!("Need {} but have {}", required, available);
            Err(Error::Order(OrderError::InsufficientBalance { required, available }))
        }

        Err(Error::Order(OrderError::InvalidPrice { price, min, max })) => {
            eprintln!("Price {} outside range [{}, {}]", price, min, max);
            Err(Error::Order(OrderError::InvalidPrice { price, min, max }))
        }

        Err(Error::Order(OrderError::InvalidSize { size, min, max })) => {
            eprintln!("Size {} outside range [{}, {}]", size, min, max);
            Err(Error::Order(OrderError::InvalidSize { size, min, max }))
        }

        Err(Error::Order(OrderError::WouldImmediatelyMatch)) => {
            eprintln!("Post-only order would cross spread");
            Err(Error::Order(OrderError::WouldImmediatelyMatch))
        }

        Err(Error::Order(OrderError::SymbolNotFound(sym))) => {
            eprintln!("Unknown symbol: {}", sym);
            Err(Error::Order(OrderError::SymbolNotFound(sym)))
        }

        // Rate limiting
        Err(Error::RateLimit { retry_after }) => {
            eprintln!("Rate limited, retry after {:?}", retry_after);
            tokio::time::sleep(retry_after).await;
            client.place_order(order).await
        }

        // Other errors
        Err(e) => {
            eprintln!("Unexpected error: {}", e);
            Err(e)
        }
    }
}

High-Frequency Trading Pattern

use lux_dex_sdk::{Client, Order, OrderId};
use std::sync::Arc;
use tokio::sync::mpsc;

struct HftOrderManager {
    client: Arc<Client>,
    order_tx: mpsc::Sender<Order>,
}

impl HftOrderManager {
    pub fn new(client: Arc<Client>, concurrency: usize) -> Self {
        let (tx, mut rx) = mpsc::channel::<Order>(10_000);

        // Spawn worker tasks
        for _ in 0..concurrency {
            let client = Arc::clone(&client);
            let mut rx = rx.clone();
            tokio::spawn(async move {
                while let Some(order) = rx.recv().await {
                    if let Err(e) = client.place_order(order).await {
                        eprintln!("Order failed: {}", e);
                    }
                }
            });
        }

        Self {
            client,
            order_tx: tx,
        }
    }

    /// Non-blocking order submission
    pub fn submit(&self, order: Order) -> Result<()> {
        self.order_tx.try_send(order)
            .map_err(|_| Error::QueueFull)
    }
}

Complete Example

use lux_dex_sdk::{Client, Order, OrderType, Side, TimeInForce, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = Client::builder()
        .json_rpc_url("http://localhost:8080/rpc")
        .build()
        .await?;

    // Place a bracket order (entry + take profit + stop loss)
    let entry = Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Buy)
        .price(50_000.0)
        .size(1.0)
        .build()?;

    let entry_response = client.place_order(entry).await?;
    println!("Entry order: {}", entry_response.id);

    // Take profit
    let take_profit = Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::Limit)
        .side(Side::Sell)
        .price(55_000.0)
        .size(1.0)
        .reduce_only(true)
        .build()?;

    // Stop loss
    let stop_loss = Order::builder()
        .symbol("BTC-USD")
        .order_type(OrderType::StopLimit)
        .side(Side::Sell)
        .stop_price(48_000.0)
        .price(47_900.0)
        .size(1.0)
        .reduce_only(true)
        .build()?;

    let results = client.place_orders_batch(vec![take_profit, stop_loss]).await?;

    for result in results {
        match result {
            Ok(r) => println!("Exit order: {}", r.id),
            Err(e) => eprintln!("Failed: {}", e),
        }
    }

    Ok(())
}