ostium-rust-sdk 0.1.0

Rust SDK for interacting with the Ostium trading platform on Arbitrum
Documentation
//! Trading example for the Ostium Rust SDK
//!
//! This example demonstrates how to:
//! - Create a client with a private key
//! - Check account balance
//! - Open a trading position
//! - Update take profit and stop loss
//! - Close a position
//!
//! ⚠️  WARNING: This example uses MAINNET with REAL MONEY!
//! Use a secure private key and start with small amounts for testing.

use ostium_rust_sdk::{
    ClosePositionParams, Network, OpenPositionParams, OstiumClient, PositionSide, Result,
    UpdateTPSLParams,
};
use rust_decimal_macros::dec;
use std::env;

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize logging
    tracing_subscriber::fmt::init();

    println!("🚀 Ostium Rust SDK - Trading Example (MAINNET)");
    println!("===============================================");
    println!("⚠️  WARNING: This example uses MAINNET with REAL MONEY!");
    println!("   Only proceed if you understand the risks involved.");

    // Get private key from environment variable
    let private_key = env::var("OSTIUM_PRIVATE_KEY").unwrap_or_else(|_| {
        println!("❌ CRITICAL: No OSTIUM_PRIVATE_KEY environment variable found!");
        println!("   This example requires a REAL private key for mainnet trading.");
        println!("   Set it with: export OSTIUM_PRIVATE_KEY=your_private_key_here");
        println!("   ⚠️  WARNING: This will use REAL MONEY on mainnet!");
        println!("   Exiting to prevent accidental transactions with dummy key...");
        std::process::exit(1);
    });

    // Create a client with a private key for trading
    println!("\n📡 Connecting to Ostium mainnet with signer...");
    let client = OstiumClient::builder(Network::Mainnet)
        .with_private_key(&private_key)?
        .build()
        .await?;

    println!("✅ Connected to network: {:?}", client.config().network);
    println!("🔗 RPC URL: {}", client.config().rpc_url);

    if let Some(address) = client.signer_address() {
        println!("👤 Signer address: {}", address);
    } else {
        println!("❌ No signer configured");
        return Ok(());
    }

    // Check if trading pairs are available
    println!("\n🔍 Checking mainnet trading pairs availability...");
    let pairs = client.get_pairs().await?;
    let active_pairs: Vec<_> = pairs.iter().filter(|p| p.is_active).collect();

    if active_pairs.is_empty() {
        println!("⚠️  No active trading pairs found on mainnet.");
        println!("   This is unexpected for mainnet - please check network connectivity.");
        println!("   Available pairs:");
        for (i, pair) in pairs.iter().take(5).enumerate() {
            println!(
                "  {}. {} (Status: {})",
                i + 1,
                pair.symbol,
                if pair.is_active { "Active" } else { "Inactive" }
            );
        }
    } else {
        println!("✅ Found {} active trading pairs", active_pairs.len());
        for pair in &active_pairs {
            println!("{}", pair.symbol);
        }
    }

    // Choose a symbol to test with - prefer an active pair if available
    let test_symbol = if active_pairs.is_empty() {
        "BTC/USD".to_string() // Fallback to BTC/USD for demonstration
    } else {
        active_pairs[0].symbol.clone() // Use the first active pair
    };

    // Check account balance
    println!("\n💳 Checking account balance...");
    match client.get_balance(None).await {
        Ok(balance) => {
            println!("✅ Account Balance:");
            println!("  • Asset: {}", balance.asset);
            println!("  • Available: {}", balance.available);
            println!("  • Locked: {}", balance.locked);
            println!("  • Total: {}", balance.total);

            if balance.total < dec!(100) {
                println!("⚠️  Low balance detected. You may need more USDC for trading.");
                println!("   Current balance: {} USDC", balance.total);
                println!("   Recommended minimum: 100 USDC for safe testing");
            }
        }
        Err(e) => {
            println!("⚠️  Failed to fetch balance: {}", e);
        }
    }

    // Get current positions
    println!("\n📊 Checking current positions...");
    match client.get_positions(None).await {
        Ok(positions) => {
            if positions.is_empty() {
                println!("✅ No open positions");
            } else {
                println!("✅ Found {} open positions:", positions.len());
                for position in &positions {
                    println!(
                        "{} {} {} @ ${}",
                        position.symbol,
                        match position.side {
                            PositionSide::Long => "LONG",
                            PositionSide::Short => "SHORT",
                        },
                        position.size,
                        position.entry_price
                    );
                }
            }
        }
        Err(e) => {
            println!("⚠️  Failed to fetch positions: {}", e);
        }
    }

    // Get current price for the test symbol
    println!("\n📊 Getting current {} price...", test_symbol);
    let current_price = match client.get_price(&test_symbol).await {
        Ok(price) => {
            println!("✅ Current {} price: ${}", test_symbol, price.mark_price);
            price.mark_price
        }
        Err(e) => {
            println!("⚠️  Failed to get price: {}", e);
            println!("   Using default price for demonstration");
            dec!(50000) // Default price for demo
        }
    };

    // Example: Open a long position
    println!(
        "\n📈 Example: Opening a long position on {}...",
        test_symbol
    );
    println!("⚠️  WARNING: This will place a REAL trade with REAL money!");

    let open_params = OpenPositionParams {
        symbol: test_symbol.clone(),
        side: PositionSide::Long,
        size: dec!(0.001),   // Very small size for safety: 0.001 BTC
        leverage: dec!(2.0), // Conservative 2x leverage
        take_profit: Some(current_price * dec!(1.05)), // 5% profit target
        stop_loss: Some(current_price * dec!(0.95)), // 5% stop loss
        slippage_tolerance: dec!(0.02), // 2% slippage tolerance
    };

    println!("📋 Position Parameters:");
    println!("  • Symbol: {}", open_params.symbol);
    println!("  • Side: {:?}", open_params.side);
    println!("  • Size: {} (VERY SMALL for safety)", open_params.size);
    println!("  • Leverage: {}x (Conservative)", open_params.leverage);
    println!(
        "  • Take Profit: ${} (5% gain)",
        open_params.take_profit.unwrap_or_default()
    );
    println!(
        "  • Stop Loss: ${} (5% loss protection)",
        open_params.stop_loss.unwrap_or_default()
    );
    println!(
        "  • Slippage Tolerance: {}%",
        open_params.slippage_tolerance * dec!(100)
    );

    match client.open_position(open_params).await {
        Ok(tx_hash) => {
            println!("✅ Position opened successfully!");
            println!("  • Transaction Hash: {}", tx_hash);

            // Example position ID (in real implementation, this would come from events)
            let position_id = format!("{}:0:0", client.signer_address().unwrap());

            // Example: Update take profit and stop loss
            println!("\n🎯 Example: Updating take profit and stop loss...");
            let update_params = UpdateTPSLParams {
                position_id: position_id.clone(),
                take_profit: Some(current_price * dec!(1.08)), // New take profit at 8% gain
                stop_loss: Some(current_price * dec!(0.92)),   // New stop loss at 8% loss
            };

            println!("📋 Update Parameters:");
            println!("  • Position ID: {}", update_params.position_id);
            println!(
                "  • New Take Profit: ${} (8% gain)",
                update_params.take_profit.unwrap_or_default()
            );
            println!(
                "  • New Stop Loss: ${} (8% loss protection)",
                update_params.stop_loss.unwrap_or_default()
            );

            match client.update_tp_sl(update_params).await {
                Ok(tx_hash) => {
                    println!("✅ TP/SL updated successfully!");
                    println!("  • Transaction Hash: {}", tx_hash);
                }
                Err(e) => {
                    println!("⚠️  Failed to update TP/SL: {}", e);
                    if e.to_string().contains("execution reverted") {
                        println!("   This is expected when the position doesn't exist or has already been executed.");
                    }
                }
            }

            // Example: Close the position
            println!("\n🔒 Example: Closing the position...");
            let close_params = ClosePositionParams {
                position_id,
                size: None,                     // Close entire position
                slippage_tolerance: dec!(0.02), // 2% slippage tolerance
            };

            println!("📋 Close Parameters:");
            println!("  • Position ID: {}", close_params.position_id);
            println!("  • Size: Full position");
            println!("  • Slippage Tolerance: 2%");

            match client.close_position(close_params).await {
                Ok(tx_hash) => {
                    println!("✅ Position closed successfully!");
                    println!("  • Transaction Hash: {}", tx_hash);
                }
                Err(e) => {
                    println!("⚠️  Failed to close position: {}", e);
                    if e.to_string().contains("execution reverted") {
                        println!("   This is expected when the position doesn't exist or has already been executed.");
                    }
                }
            }
        }
        Err(e) => {
            println!("⚠️  Failed to open position: {}", e);
            if e.to_string().contains("Failed to get pair index") {
                println!("   This indicates the trading pair may not be properly configured.");
                println!("   Please check if the symbol is correct and the pair is active.");
            } else {
                println!("   This might be due to:");
                println!("   • Insufficient balance");
                println!("   • Invalid private key");
                println!("   • Network issues");
                println!("   • Contract interaction errors");
                println!("   • Market conditions");
            }
        }
    }

    println!("\n✨ Trading example completed!");
    println!("\n📚 CRITICAL SAFETY REMINDERS:");
    println!("   🚨 This example uses MAINNET - REAL MONEY IS INVOLVED!");
    println!("   🚨 Positions opened will be actual trades with real funds");
    println!("   🚨 Always verify your private key and account balance before running");
    println!("   🚨 Start with very small amounts to test functionality");
    println!("   🚨 Keep your private keys secure and never commit them to code");
    println!("   🚨 Monitor your positions actively");
    println!("   🚨 Use appropriate risk management strategies");
    println!("   🚨 Consider market conditions and volatility");
    println!("   🚨 Never risk more than you can afford to lose");
    println!("\n🔧 Technical Notes:");
    println!("   • Symbol format: Use 'BTC/USD' format - the SDK handles conversion internally");
    println!("   • Position sizes are intentionally small for safety");
    println!("   • Leverage is kept conservative (2x) for risk management");
    println!("   • Stop losses are set to limit potential losses");
    println!("   • Always test on testnet first before using mainnet");

    Ok(())
}