Rust SDK
Account Management
Account queries, balance types, positions, and portfolio management with the Rust SDK
Account Management
The Rust SDK provides type-safe account management with comprehensive balance tracking, position management, and portfolio analytics.
Get Account Info
Basic Query
use lux_dex_sdk::{Client, Result};
async fn get_account(client: &Client) -> Result<()> {
let account = client.get_account().await?;
println!("Account ID: {}", account.id);
println!("Status: {:?}", account.status);
println!("Created: {}", account.created_at);
Ok(())
}Account Types
/// User account information
#[derive(Debug, Clone)]
pub struct Account {
pub id: AccountId,
pub status: AccountStatus,
pub tier: AccountTier,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub trading_enabled: bool,
pub margin_enabled: bool,
pub maker_fee: f64,
pub taker_fee: f64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AccountStatus {
Active,
Suspended,
Restricted,
PendingVerification,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AccountTier {
Standard,
Pro,
Institutional,
MarketMaker,
}Balances
Get All Balances
async fn get_balances(client: &Client) -> Result<()> {
let balances = client.get_balances().await?;
for balance in balances {
if balance.total() > 0.0 {
println!(
"{}: available={:.8}, hold={:.8}, total={:.8}",
balance.currency,
balance.available,
balance.hold,
balance.total()
);
}
}
Ok(())
}Get Specific Balance
let btc = client.get_balance("BTC").await?;
println!("BTC available: {}", btc.available);
println!("BTC on hold: {}", btc.hold);Balance Types
/// Currency balance
#[derive(Debug, Clone)]
pub struct Balance {
pub currency: String,
pub available: f64, // Available for trading
pub hold: f64, // Reserved for open orders
pub pending: f64, // Pending deposits/withdrawals
pub staked: f64, // Staked amount
}
impl Balance {
/// Total balance
pub fn total(&self) -> f64 {
self.available + self.hold + self.pending + self.staked
}
/// Tradeable balance (excludes pending)
pub fn tradeable(&self) -> f64 {
self.available + self.hold
}
}
/// Detailed balance with valuation
#[derive(Debug, Clone)]
pub struct DetailedBalance {
pub balance: Balance,
pub usd_value: f64,
pub btc_value: f64,
pub price: f64,
pub change_24h: f64,
}Balance with USD Valuation
async fn get_portfolio_value(client: &Client) -> Result<f64> {
let balances = client.get_balances_with_valuation().await?;
let total_usd: f64 = balances.iter()
.map(|b| b.usd_value)
.sum();
println!("Portfolio value: ${:.2}", total_usd);
// Top holdings
let mut sorted = balances;
sorted.sort_by(|a, b| b.usd_value.partial_cmp(&a.usd_value).unwrap());
println!("\nTop holdings:");
for balance in sorted.iter().take(5) {
println!(
" {}: ${:.2} ({:.2}%)",
balance.balance.currency,
balance.usd_value,
balance.usd_value / total_usd * 100.0
);
}
Ok(total_usd)
}Positions
Get Open Positions
async fn get_positions(client: &Client) -> Result<()> {
let positions = client.get_positions().await?;
for pos in positions {
let pnl = pos.unrealized_pnl();
let pnl_pct = pos.unrealized_pnl_percent();
println!(
"{} | {} {:.4} @ {:.2} | PnL: ${:.2} ({:.2}%)",
pos.symbol,
pos.side,
pos.size.abs(),
pos.entry_price,
pnl,
pnl_pct
);
}
Ok(())
}Position Types
/// Trading position
#[derive(Debug, Clone)]
pub struct Position {
pub symbol: String,
pub side: PositionSide,
pub size: f64, // Positive for long, negative for short
pub entry_price: f64, // Average entry price
pub mark_price: f64, // Current market price
pub liquidation_price: Option<f64>,
pub margin: f64,
pub leverage: f64,
pub realized_pnl: f64,
pub funding_fee: f64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PositionSide {
Long,
Short,
Both, // Hedge mode
}
impl Position {
/// Unrealized P&L
pub fn unrealized_pnl(&self) -> f64 {
(self.mark_price - self.entry_price) * self.size
}
/// Unrealized P&L percentage
pub fn unrealized_pnl_percent(&self) -> f64 {
if self.entry_price == 0.0 {
return 0.0;
}
(self.mark_price - self.entry_price) / self.entry_price * 100.0
}
/// Total P&L (realized + unrealized)
pub fn total_pnl(&self) -> f64 {
self.realized_pnl + self.unrealized_pnl()
}
/// Position value at current price
pub fn notional_value(&self) -> f64 {
self.size.abs() * self.mark_price
}
/// Return on equity
pub fn roe(&self) -> f64 {
if self.margin == 0.0 {
return 0.0;
}
self.unrealized_pnl() / self.margin * 100.0
}
}Position Summary
async fn position_summary(client: &Client) -> Result<()> {
let positions = client.get_positions().await?;
let total_pnl: f64 = positions.iter()
.map(|p| p.unrealized_pnl())
.sum();
let total_notional: f64 = positions.iter()
.map(|p| p.notional_value())
.sum();
let total_margin: f64 = positions.iter()
.map(|p| p.margin)
.sum();
println!("Position Summary:");
println!(" Count: {}", positions.len());
println!(" Total Notional: ${:.2}", total_notional);
println!(" Total Margin: ${:.2}", total_margin);
println!(" Total Unrealized PnL: ${:.2}", total_pnl);
println!(" Average ROE: {:.2}%", total_pnl / total_margin * 100.0);
Ok(())
}Transaction History
Get Deposits/Withdrawals
use lux_dex_sdk::TransactionFilter;
async fn get_transactions(client: &Client) -> Result<()> {
let filter = TransactionFilter {
currency: Some("BTC".into()),
tx_type: Some(TransactionType::Deposit),
start_time: Some(Utc::now() - Duration::days(30)),
limit: Some(100),
..Default::default()
};
let transactions = client.get_transactions(filter).await?;
for tx in transactions {
println!(
"{} | {} {} | {} | {}",
tx.created_at.format("%Y-%m-%d"),
tx.tx_type,
tx.amount,
tx.currency,
tx.status
);
}
Ok(())
}Transaction Types
/// Account transaction
#[derive(Debug, Clone)]
pub struct Transaction {
pub id: TransactionId,
pub tx_type: TransactionType,
pub currency: String,
pub amount: f64,
pub fee: f64,
pub status: TransactionStatus,
pub address: Option<String>,
pub tx_hash: Option<String>,
pub created_at: DateTime<Utc>,
pub completed_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TransactionType {
Deposit,
Withdrawal,
Transfer,
Trade,
Fee,
Rebate,
Funding,
Liquidation,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TransactionStatus {
Pending,
Processing,
Completed,
Failed,
Cancelled,
}Trade History
Get Fills
async fn get_fills(client: &Client) -> Result<()> {
let fills = client.get_fills("BTC-USD", 100).await?;
let mut total_bought = 0.0;
let mut total_sold = 0.0;
let mut total_fees = 0.0;
for fill in &fills {
match fill.side {
Side::Buy => total_bought += fill.size,
Side::Sell => total_sold += fill.size,
}
total_fees += fill.fee;
}
println!("Trade Summary (BTC-USD):");
println!(" Total bought: {:.8} BTC", total_bought);
println!(" Total sold: {:.8} BTC", total_sold);
println!(" Total fees: ${:.2}", total_fees);
Ok(())
}Fill Types
/// Trade fill (execution)
#[derive(Debug, Clone)]
pub struct Fill {
pub id: FillId,
pub order_id: OrderId,
pub trade_id: TradeId,
pub symbol: String,
pub side: Side,
pub price: f64,
pub size: f64,
pub fee: f64,
pub fee_currency: String,
pub liquidity: Liquidity,
pub timestamp: DateTime<Utc>,
}Portfolio Analytics
Portfolio Manager
/// Portfolio analytics and management
pub struct PortfolioManager {
client: Arc<Client>,
}
impl PortfolioManager {
pub fn new(client: Arc<Client>) -> Self {
Self { client }
}
/// Get portfolio allocation
pub async fn allocation(&self) -> Result<Vec<Allocation>> {
let balances = self.client.get_balances_with_valuation().await?;
let total: f64 = balances.iter().map(|b| b.usd_value).sum();
Ok(balances.into_iter()
.filter(|b| b.usd_value > 0.0)
.map(|b| Allocation {
currency: b.balance.currency,
value: b.usd_value,
percent: b.usd_value / total * 100.0,
})
.collect())
}
/// Get portfolio performance
pub async fn performance(&self, period: Duration) -> Result<Performance> {
let history = self.client
.get_portfolio_history(Utc::now() - period, Utc::now())
.await?;
if history.is_empty() {
return Err(Error::InsufficientData);
}
let start_value = history.first().unwrap().value;
let end_value = history.last().unwrap().value;
let returns = (end_value - start_value) / start_value;
// Calculate daily returns for volatility
let daily_returns: Vec<f64> = history.windows(2)
.map(|w| (w[1].value - w[0].value) / w[0].value)
.collect();
let volatility = std_dev(&daily_returns) * (365.0_f64).sqrt();
Ok(Performance {
period,
start_value,
end_value,
returns,
volatility,
sharpe: returns / volatility,
max_drawdown: max_drawdown(&history),
})
}
}
#[derive(Debug, Clone)]
pub struct Allocation {
pub currency: String,
pub value: f64,
pub percent: f64,
}
#[derive(Debug, Clone)]
pub struct Performance {
pub period: Duration,
pub start_value: f64,
pub end_value: f64,
pub returns: f64,
pub volatility: f64,
pub sharpe: f64,
pub max_drawdown: f64,
}
fn std_dev(values: &[f64]) -> f64 {
if values.is_empty() {
return 0.0;
}
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter()
.map(|x| (x - mean).powi(2))
.sum::<f64>() / values.len() as f64;
variance.sqrt()
}
fn max_drawdown(history: &[PortfolioSnapshot]) -> f64 {
let mut peak = 0.0;
let mut max_dd = 0.0;
for snapshot in history {
if snapshot.value > peak {
peak = snapshot.value;
}
let dd = (peak - snapshot.value) / peak;
if dd > max_dd {
max_dd = dd;
}
}
max_dd
}Risk Management
Margin Info
async fn check_margin(client: &Client) -> Result<()> {
let margin = client.get_margin_info().await?;
println!("Margin Info:");
println!(" Equity: ${:.2}", margin.equity);
println!(" Used Margin: ${:.2}", margin.used_margin);
println!(" Free Margin: ${:.2}", margin.free_margin);
println!(" Margin Level: {:.2}%", margin.margin_level * 100.0);
if margin.margin_level < 1.5 {
println!("WARNING: Low margin level!");
}
Ok(())
}Margin Types
/// Margin account information
#[derive(Debug, Clone)]
pub struct MarginInfo {
pub equity: f64,
pub used_margin: f64,
pub free_margin: f64,
pub margin_level: f64, // equity / used_margin
pub maintenance_margin: f64,
pub initial_margin: f64,
pub unrealized_pnl: f64,
pub available_balance: f64,
}
impl MarginInfo {
/// Check if position can be opened
pub fn can_open_position(&self, required_margin: f64) -> bool {
self.free_margin >= required_margin
}
/// Distance to margin call (percentage)
pub fn margin_call_distance(&self) -> f64 {
if self.maintenance_margin == 0.0 {
return f64::INFINITY;
}
(self.equity - self.maintenance_margin) / self.equity * 100.0
}
}WebSocket Account Updates
Subscribe to Balance Updates
use tokio_stream::StreamExt;
async fn stream_balances(client: &Client) -> Result<()> {
let mut stream = client.subscribe_balance_updates().await?;
while let Some(update) = stream.next().await {
let update = update?;
println!(
"Balance update: {} available={:.8} hold={:.8}",
update.currency,
update.available,
update.hold
);
}
Ok(())
}Subscribe to Position Updates
async fn stream_positions(client: &Client) -> Result<()> {
let mut stream = client.subscribe_position_updates().await?;
while let Some(update) = stream.next().await {
let pos = update?;
println!(
"Position: {} {} @ {:.2} | PnL: ${:.2}",
pos.symbol,
pos.side,
pos.entry_price,
pos.unrealized_pnl()
);
}
Ok(())
}Complete Example
use lux_dex_sdk::{Client, Result};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<()> {
let client = Arc::new(
Client::builder()
.json_rpc_url("http://localhost:8080/rpc")
.websocket_url("ws://localhost:8081")
.api_key(std::env::var("LUX_API_KEY")?)
.api_secret(std::env::var("LUX_API_SECRET")?)
.build()
.await?
);
// Get account info
let account = client.get_account().await?;
println!("Account: {} ({})", account.id, account.tier);
// Portfolio summary
let portfolio = PortfolioManager::new(Arc::clone(&client));
let allocation = portfolio.allocation().await?;
println!("\nPortfolio Allocation:");
for alloc in allocation.iter().take(5) {
println!(" {}: ${:.2} ({:.1}%)",
alloc.currency, alloc.value, alloc.percent);
}
// Check positions
let positions = client.get_positions().await?;
if !positions.is_empty() {
println!("\nOpen Positions:");
for pos in &positions {
println!(" {} {} @ {:.2} | PnL: ${:.2} ({:.2}%)",
pos.symbol,
pos.side,
pos.entry_price,
pos.unrealized_pnl(),
pos.unrealized_pnl_percent()
);
}
}
// Check margin
let margin = client.get_margin_info().await?;
println!("\nMargin Status:");
println!(" Free: ${:.2}", margin.free_margin);
println!(" Level: {:.0}%", margin.margin_level * 100.0);
// Stream updates
let client_clone = Arc::clone(&client);
tokio::spawn(async move {
let mut stream = client_clone.subscribe_balance_updates().await.unwrap();
while let Some(update) = stream.next().await {
if let Ok(bal) = update {
println!("[Balance] {}: {:.8}", bal.currency, bal.available);
}
}
});
// Keep running
tokio::signal::ctrl_c().await?;
Ok(())
}