//! ICMarkets FIX 4.4 Integration Demo //! //! This example demonstrates how to use the ICMarkets FIX client for high-frequency trading use std::time::Duration; use tokio::sync::mpsc; use tracing::{error, info}; use trading_engine::brokers::{config::ICMarketsConfig, ICMarketsClient}; use trading_engine::prelude::{Side, TradingOrder}; use trading_engine::trading::data_interface::{BrokerInterface, ExecutionReport}; use trading_engine::trading_operations::OrderType; use uuid::Uuid; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize logging tracing::subscriber::set_global_default( tracing_subscriber::fmt().with_env_filter("info").finish(), ) .expect("Failed to set subscriber"); // Note: tracing_subscriber added to dev-dependencies for examples info!("Starting ICMarkets FIX 4.4 integration demo"); // Configure ICMarkets connection let config = ICMarketsConfig { enabled: true, fix_endpoint: "fix-demo.icmarkets.com".to_string(), fix_port: 9880, sender_comp_id: "DEMO_CLIENT".to_string(), target_comp_id: "ICMARKETS".to_string(), rest_base_url: "https://api-demo.icmarkets.com".to_string(), rate_limit_per_minute: 60, username: Some("demo_user".to_string()), password: std::env::var("FOXHUNT_IC_PASSWORD") .ok() .or_else(|| std::env::var("IC_PASSWORD").ok()), account_id: Some("DEMO_ACCOUNT".to_string()), }; // Create FIX client let mut client = ICMarketsClient::new(config); // Set up execution report callback let mut exec_rx = client.subscribe_executions().await?; // Start execution report handler tokio::spawn(async move { while let Some(execution) = exec_rx.recv().await { info!( "🎯 Execution Report: {} {} {} @ ${:.4}", execution.symbol, execution.filled_quantity, match execution.side { OrderSide::Buy => "BUY", OrderSide::Sell => "SELL", }, execution.average_price.map(|p| p.to_f64()).unwrap_or(0.0) ); } }); // Connect to ICMarkets info!("🔌 Connecting to ICMarkets FIX server..."); if let Err(e) = client.connect().await { error!("❌ Failed to connect: {}", e); return Err(Box::new(e) as Box); } info!("✅ Connected successfully!"); // Wait for session to be established tokio::time::sleep(Duration::from_secs(2)).await; // Demo trading operations if let Err(e) = demo_trading_operations(&client).await { error!("❌ Trading operations failed: {}", e); return Err(e); } // Keep running for a while to receive messages info!("⏳ Running for 30 seconds to demonstrate message handling..."); tokio::time::sleep(Duration::from_secs(30)).await; // Disconnect info!("🔌 Disconnecting..."); info!("✅ Connected successfully!"); // Wait for session to be established tokio::time::sleep(Duration::from_secs(2)).await; // Demo trading operations demo_trading_operations(&client).await?; // Keep running for a while to receive messages info!("⏳ Running for 30 seconds to demonstrate message handling..."); tokio::time::sleep(Duration::from_secs(30)).await; // Disconnect info!("🔌 Disconnecting..."); client .disconnect() .await .map_err(|e| Box::new(e) as Box)?; info!("✅ Demo completed successfully!"); Ok(()) } async fn demo_trading_operations( client: &ICMarketsClient, ) -> Result<(), Box> { info!("🎯 Starting trading operations demo"); // Example 1: Market Buy Order info!("📈 Submitting market buy order for EUR/USD"); let market_buy_order = TradingOrder { id: format!("MKT_BUY_{}", Uuid::new_v4().simple()), symbol: "EURUSD".to_string(), side: Side::Buy, order_type: OrderType::Market, quantity: Decimal::new(10000, 0), // 10k units price: Decimal::ZERO, time_in_force: core::trading_operations::TimeInForce::ImmediateOrCancel, metadata: std::collections::HashMap::new(), created_at: chrono::Utc::now(), submitted_at: None, executed_at: None, status: core::trading_operations::OrderStatus::Created, fill_quantity: Decimal::ZERO, average_fill_price: None, }; let order_id1 = client.submit_order(&market_buy_order).await?; info!("✅ Market buy order submitted: {}", order_id1); // Wait a bit tokio::time::sleep(Duration::from_millis(500)).await; // Example 2: Limit Sell Order info!("📉 Submitting limit sell order for EUR/USD"); let limit_sell_order = TradingOrder { id: format!("LMT_SELL_{}", Uuid::new_v4().simple()), symbol: "EURUSD".to_string(), side: Side::Sell, order_type: OrderType::Limit, quantity: Decimal::new(15000, 0), // 15k units price: Decimal::new(10950, 4), // 1.0950 limit price time_in_force: core::trading_operations::TimeInForce::GoodTillCancel, metadata: std::collections::HashMap::new(), created_at: chrono::Utc::now(), submitted_at: None, executed_at: None, status: core::trading_operations::OrderStatus::Created, fill_quantity: rust_decimal::Decimal::ZERO, average_fill_price: None, }; let order_id2 = client.submit_order(&limit_sell_order).await?; info!("✅ Limit sell order submitted: {}", order_id2); // Wait a bit tokio::time::sleep(Duration::from_millis(500)).await; // Example 3: Stop Loss Order info!("🛑 Submitting stop loss order for EUR/USD"); let stop_order = TradingOrder { id: format!("STOP_{}", Uuid::new_v4().simple()), symbol: "EURUSD".to_string(), side: Side::Sell, order_type: OrderType::Stop, quantity: Decimal::new(10000, 0), price: Decimal::new(10800, 4), // 1.0800 stop price time_in_force: core::trading_operations::TimeInForce::GoodTillCancel, metadata: std::collections::HashMap::new(), created_at: chrono::Utc::now(), submitted_at: None, executed_at: None, status: core::trading_operations::OrderStatus::Created, fill_quantity: rust_decimal::Decimal::ZERO, average_fill_price: None, }; let order_id3 = client.submit_order(&stop_order).await?; info!("✅ Stop loss order submitted: {}", order_id3); // Wait a bit tokio::time::sleep(Duration::from_secs(1)).await; // Example 4: Cancel an order info!("❌ Cancelling limit sell order"); client.cancel_order(&order_id2).await?; info!("✅ Cancel request submitted for order: {}", order_id2); // Check order statuses tokio::time::sleep(Duration::from_millis(500)).await; match client.get_order_status(&order_id1).await { Ok(status) => info!("📊 Order {} status: {:?}", order_id1, status), Err(e) => error!("Failed to get status for order {}: {}", order_id1, e), } match client.get_order_status(&order_id2).await { Ok(status) => info!("📊 Order {} status: {:?}", order_id2, status), Err(e) => error!("Failed to get status for order {}: {}", order_id2, e), } match client.get_order_status(&order_id3).await { Ok(status) => info!("📊 Order {} status: {:?}", order_id3, status), Err(e) => error!("Failed to get status for order {}: {}", order_id3, e), } // Example 5: Multiple Currency Pairs info!("🌍 Submitting orders for multiple currency pairs"); let pairs = vec![ ("GBPUSD", 1.2650, 8000.0), ("USDJPY", 149.50, 1000000.0), ("USDCHF", 0.8950, 12000.0), ("AUDUSD", 0.6750, 15000.0), ]; for (symbol, price, qty) in pairs { let order = TradingOrder { id: format!("MULTI_{}_{}", symbol, Uuid::new_v4().simple()), symbol: symbol.to_string(), side: Side::Buy, order_type: OrderType::Limit, quantity: Decimal::new((qty * 10000.0) as i64, 4), // Convert to decimal price: Decimal::new((price * 10000.0) as i64, 4), // Convert to decimal time_in_force: core::trading_operations::TimeInForce::GoodTillCancel, metadata: std::collections::HashMap::new(), created_at: chrono::Utc::now(), submitted_at: None, executed_at: None, status: core::trading_operations::OrderStatus::Created, fill_quantity: Decimal::ZERO, average_fill_price: None, }; match client.submit_order(&order).await { Ok(order_id) => info!("✅ {} order submitted: {}", symbol, order_id), Err(e) => error!("❌ Failed to submit {} order: {}", symbol, e), } // Small delay between orders to avoid overwhelming the server tokio::time::sleep(Duration::from_millis(100)).await; } info!("🎯 Trading operations demo completed"); Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_config_creation() { let config = ICMarketsConfig { enabled: true, fix_endpoint: "fix-demo.icmarkets.com".to_string(), fix_port: 9880, sender_comp_id: "DEMO_CLIENT".to_string(), target_comp_id: "ICMARKETS".to_string(), rest_base_url: "https://api-demo.icmarkets.com".to_string(), rate_limit_per_minute: 60, username: Some("demo_user".to_string()), password: std::env::var("FOXHUNT_IC_PASSWORD") .ok() .or_else(|| std::env::var("IC_PASSWORD").ok()), account_id: Some("DEMO_ACCOUNT".to_string()), }; assert_eq!(config.fix_endpoint, "fix-demo.icmarkets.com"); assert_eq!(config.fix_port, 9880); assert!(config.enabled); } #[test] fn test_order_creation() { let order = TradingOrder { id: "TEST_ORDER_001".to_string(), symbol: "EURUSD".to_string(), side: Side::Buy, order_type: OrderType::Limit, quantity: Decimal::new(10000, 0), price: Decimal::new(10900, 4), // 1.0900 time_in_force: core::trading_operations::TimeInForce::GoodTillCancel, metadata: std::collections::HashMap::new(), created_at: chrono::Utc::now(), submitted_at: None, executed_at: None, status: core::trading_operations::OrderStatus::Created, fill_quantity: Decimal::ZERO, average_fill_price: None, }; assert_eq!(order.symbol, "EURUSD"); assert_eq!(order.quantity, Decimal::new(10000, 0)); assert_eq!(order.price, Decimal::new(10900, 4)); } #[tokio::test] async fn test_client_creation() { let config = ICMarketsConfig { enabled: true, fix_endpoint: "fix-demo.icmarkets.com".to_string(), fix_port: 9880, sender_comp_id: "DEMO_CLIENT".to_string(), target_comp_id: "ICMARKETS".to_string(), rest_base_url: "https://api-demo.icmarkets.com".to_string(), rate_limit_per_minute: 60, username: Some("demo_user".to_string()), password: std::env::var("FOXHUNT_IC_PASSWORD") .ok() .or_else(|| std::env::var("IC_PASSWORD").ok()), account_id: Some("DEMO_ACCOUNT".to_string()), }; let client = ICMarketsClient::new(config); // Client should be created successfully // Note: Cannot access internal config directly, test creation succeeds } }