kraky 0.1.2

Lightweight, production-ready Rust SDK for Kraken Exchange WebSocket API v2 with unique orderbook imbalance detection and WebSocket trading
Documentation
//! Authentication Example
//!
//! This example demonstrates the authentication module for Kraken WebSocket API.
//!
//! ## Features Showcased
//! - HMAC-SHA256 token generation
//! - Credentials management
//! - Private channel message structures
//!
//! ## Setup
//!
//! You'll need API credentials from Kraken:
//! 1. Log into kraken.com
//! 2. Settings → API → Create API Key
//! 3. Set permissions (view balances, view orders, etc.)
//! 4. Save the API Key and API Secret
//!
//! ## Environment Variables
//! ```bash
//! export KRAKEN_API_KEY="your_api_key"
//! export KRAKEN_API_SECRET="your_base64_secret"
//! ```
//!
//! ## Run
//! ```bash
//! cargo run --example auth_example --features private
//! ```
//!
//! Without credentials, runs in demo mode to show the authentication flow.
//!
//! ## Note
//! This example shows the authentication infrastructure.
//! Full WebSocket integration is in development.

use kraky::{BalanceUpdate, Credentials, ExecutionUpdate, OrderUpdate};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("\n╔══════════════════════════════════════════════════════════════╗");
    println!("║       🔐 Kraky Authentication Example                        ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    // ═══════════════════════════════════════════════════════════════════════
    // STEP 1: Create Credentials
    // ═══════════════════════════════════════════════════════════════════════

    println!("═══════════════════════════════════════════════════════════════");
    println!("  STEP 1: Creating Credentials");
    println!("═══════════════════════════════════════════════════════════════\n");

    // Load credentials from environment variables or use demo values
    let api_key =
        std::env::var("KRAKEN_API_KEY").unwrap_or_else(|_| "DEMO_API_KEY_NOT_REAL".to_string());
    let api_secret = std::env::var("KRAKEN_API_SECRET")
        .unwrap_or_else(|_| "DEMO_API_SECRET_NOT_REAL_BASE64".to_string());

    let is_demo = api_key.starts_with("DEMO_");

    if is_demo {
        println!("⚠️  Running in DEMO mode (no real credentials)");
        println!("   To use real credentials, set environment variables:");
        println!("   export KRAKEN_API_KEY=\"your_api_key\"");
        println!("   export KRAKEN_API_SECRET=\"your_base64_secret\"\n");
    }

    let credentials = Credentials::new(api_key, api_secret);
    println!("✅ Credentials created");
    println!(
        "   API Key: {}...",
        &credentials.api_key()[..min(20, credentials.api_key().len())]
    );

    // ═══════════════════════════════════════════════════════════════════════
    // STEP 2: Generate Authentication Token
    // ═══════════════════════════════════════════════════════════════════════

    println!("\n═══════════════════════════════════════════════════════════════");
    println!("  STEP 2: Generating Authentication Token");
    println!("═══════════════════════════════════════════════════════════════\n");

    // Generate nonce (timestamp in nanoseconds)
    let nonce = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)?
        .as_nanos() as u64;

    println!("📝 Nonce generated: {}", nonce);

    // Generate HMAC-SHA256 signature
    match credentials.generate_token(nonce) {
        Ok(token) => {
            println!("✅ Token generated successfully");
            println!(
                "   Token (first 40 chars): {}...",
                &token[..min(40, token.len())]
            );
            println!("\n   This token would be sent in WebSocket subscription:");
            println!("   {{");
            println!("     \"method\": \"subscribe\",");
            println!("     \"params\": {{");
            println!("       \"channel\": \"balances\",");
            println!(
                "       \"token\": \"{}...\"",
                &token[..min(20, token.len())]
            );
            println!("     }}");
            println!("   }}");
        }
        Err(e) => {
            println!("❌ Token generation failed: {}", e);
            println!("\n   Note: Make sure your API secret is valid base64");
        }
    }

    // ═══════════════════════════════════════════════════════════════════════
    // STEP 3: Private Channel Message Structures
    // ═══════════════════════════════════════════════════════════════════════

    println!("\n═══════════════════════════════════════════════════════════════");
    println!("  STEP 3: Private Channel Message Structures");
    println!("═══════════════════════════════════════════════════════════════\n");

    // Example Balance Update (what you'd receive from Kraken)
    let balance_json = r#"{
        "channel": "balances",
        "type": "update",
        "data": [{
            "BTC": "1.5432",
            "USD": "50000.00",
            "ETH": "10.25"
        }]
    }"#;

    println!("💰 Balance Update Example:");
    match serde_json::from_str::<BalanceUpdate>(balance_json) {
        Ok(update) => {
            println!("   Channel: {}", update.channel);
            println!("   Assets in update: {:?}", update.assets());
            if let Some(btc) = update.get_balance("BTC") {
                println!("   BTC Balance: {}", btc);
            }
            if let Some(usd) = update.get_balance("USD") {
                println!("   USD Balance: ${}", usd);
            }
        }
        Err(e) => println!("   Parse error: {}", e),
    }

    // Example Order Update
    let order_json = r#"{
        "channel": "orders",
        "type": "update",
        "data": [{
            "order_id": "O12345-ABCDE-FGHIJ",
            "symbol": "BTC/USD",
            "side": "buy",
            "order_type": "limit",
            "limit_price": "95000.00",
            "order_qty": "0.5",
            "filled_qty": "0.25",
            "status": "open",
            "timestamp": "2024-01-01T00:00:00Z"
        }]
    }"#;

    println!("\n📋 Order Update Example:");
    match serde_json::from_str::<OrderUpdate>(order_json) {
        Ok(update) => {
            println!("   Channel: {}", update.channel);
            if let Some(order) = update.data.first() {
                println!("   Order ID: {}", order.order_id);
                println!("   Pair: {}", order.symbol);
                println!("   Side: {}", order.side);
                println!("   Type: {}", order.order_type);
                println!("   Status: {}", order.status);
                println!("   Filled: {}/{}", order.filled_qty, order.order_qty);
            }
            println!("   Is Open: {}", update.is_open());
        }
        Err(e) => println!("   Parse error: {}", e),
    }

    // Example Execution Update
    let execution_json = r#"{
        "channel": "executions",
        "type": "update",
        "data": [{
            "exec_id": "E12345-ABCDE",
            "order_id": "O12345-ABCDE-FGHIJ",
            "symbol": "BTC/USD",
            "side": "buy",
            "exec_qty": "0.25",
            "exec_price": "95000.00",
            "timestamp": "2024-01-01T00:00:00Z",
            "liquidity": "taker"
        }]
    }"#;

    println!("\n💥 Execution Update Example:");
    match serde_json::from_str::<ExecutionUpdate>(execution_json) {
        Ok(update) => {
            println!("   Channel: {}", update.channel);
            if let Some(exec) = update.data.first() {
                println!("   Execution ID: {}", exec.exec_id);
                println!("   Order ID: {}", exec.order_id);
                println!("   Pair: {}", exec.symbol);
                println!("   Side: {}", exec.side);
                println!("   Quantity: {} BTC", exec.exec_qty);
                println!("   Price: ${}", exec.exec_price);
                println!("   Liquidity: {}", exec.liquidity);
            }
            if let Some(value) = update.total_value() {
                println!("   Total Value: ${:.2}", value);
            }
        }
        Err(e) => println!("   Parse error: {}", e),
    }

    // ═══════════════════════════════════════════════════════════════════════
    // SUMMARY
    // ═══════════════════════════════════════════════════════════════════════

    println!("\n╔══════════════════════════════════════════════════════════════╗");
    println!("║                    📊 SUMMARY                                 ║");
    println!("╠══════════════════════════════════════════════════════════════╣");
    println!("║  Authentication Infrastructure: ✅ Complete                   ║");
    println!("║                                                               ║");
    println!("║  ✅ HMAC-SHA256 token generation                              ║");
    println!("║  ✅ Credentials management                                    ║");
    println!("║  ✅ Balance update parsing                                    ║");
    println!("║  ✅ Order update parsing                                      ║");
    println!("║  ✅ Execution update parsing                                  ║");
    println!("║                                                               ║");
    println!("║  🚧 Coming Soon:                                              ║");
    println!("║     - WebSocket subscription integration                     ║");
    println!("║     - Real-time balance monitoring                           ║");
    println!("║     - Order placement via WebSocket                          ║");
    println!("╚══════════════════════════════════════════════════════════════╝\n");

    Ok(())
}

fn min(a: usize, b: usize) -> usize {
    if a < b {
        a
    } else {
        b
    }
}