//! # Broker Connection Example //! //! Demonstrates how to connect to different brokers using the data module. //! This example shows connection setup for Interactive Brokers. use data::brokers::{IBConfig, InteractiveBrokersAdapter}; use data::{DataConfig, DataManager}; use foxhunt_core::prelude::*; use tokio::time::{timeout, Duration}; use tracing::{error, info, warn}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize logging tracing_subscriber::fmt::init(); info!("Starting broker connection example"); // Example 1: Interactive Brokers Connection if let Err(e) = interactive_brokers_example().await { warn!("Interactive Brokers example failed: {}", e); } // Example 2: Data Manager with multiple providers if let Err(e) = data_manager_example().await { warn!("Data manager example failed: {}", e); } info!("Broker connection examples completed"); Ok(()) } /// Example of connecting to Interactive Brokers TWS async fn interactive_brokers_example() -> anyhow::Result<()> { info!("=== Interactive Brokers Connection Example ==="); // Create IB configuration let config = IBConfig { host: "127.0.0.1".to_string(), port: 7497, // Paper trading port client_id: 1, account: "DU123456".to_string(), // Demo account timeout_seconds: 30, retry_attempts: 3, heartbeat_interval: 60, }; // Create adapter let mut adapter = InteractiveBrokersAdapter::new(config); info!("Created Interactive Brokers adapter"); // Attempt connection with timeout match timeout(Duration::from_secs(10), adapter.connect()).await { Ok(Ok(())) => { info!("✓ Successfully connected to Interactive Brokers TWS"); // Test basic functionality info!("Connection status: {}", adapter.is_connected()); info!("Adapter name: {}", adapter.name()); // Subscribe to market data for a test symbol let symbol = Symbol::from("AAPL"); match adapter.subscribe_market_data(symbol.clone()).await { Ok(()) => { info!("✓ Successfully subscribed to market data for {}", symbol); // Wait for some data tokio::time::sleep(Duration::from_secs(5)).await; // Unsubscribe if let Err(e) = adapter.unsubscribe_market_data(symbol).await { warn!("Failed to unsubscribe from market data: {}", e); } } Err(e) => warn!("Failed to subscribe to market data: {}", e), } // Disconnect info!("Disconnecting from Interactive Brokers..."); if let Err(e) = adapter.disconnect().await { warn!("Error during disconnect: {}", e); } else { info!("✓ Successfully disconnected"); } } Ok(Err(e)) => { warn!("Failed to connect to Interactive Brokers: {}", e); warn!("Make sure TWS or IB Gateway is running on port 7497"); return Err(e.into()); } Err(_) => { warn!("Connection to Interactive Brokers timed out"); warn!("Make sure TWS or IB Gateway is running and accepting connections"); return Err(anyhow::anyhow!("Connection timeout")); } } Ok(()) } /// Example of using the DataManager for coordinated broker and provider access async fn data_manager_example() -> anyhow::Result<()> { info!("=== Data Manager Example ==="); // Load configuration from environment or use defaults let config = DataConfig::from_env().unwrap_or_else(|_| { warn!("Failed to load config from environment, using defaults"); DataConfig::default() }); info!( "Loaded data configuration for environment: {}", config.environment ); // Initialize data manager let mut data_manager = DataManager::new(config).await?; info!("✓ Created data manager"); // Start all configured providers and brokers match data_manager.start().await { Ok(()) => { info!("✓ Successfully started data manager"); // Subscribe to market data events let mut market_data_rx = data_manager.subscribe_market_data_events(); let mut order_update_rx = data_manager.subscribe_order_update_events(); // Subscribe to market data for some symbols let subscription = data::types::Subscription::quotes(vec![ "SPY".to_string(), "QQQ".to_string(), "AAPL".to_string(), ]); if let Err(e) = data_manager.subscribe_market_data(subscription).await { warn!("Failed to subscribe to market data: {}", e); } else { info!("✓ Subscribed to market data"); } // Listen for events for a short time info!("Listening for market data events for 10 seconds..."); let listen_timeout = timeout(Duration::from_secs(10), async { let mut event_count = 0; loop { tokio::select! { market_event = market_data_rx.recv() => { match market_event { Ok(event) => { event_count += 1; if event_count <= 5 { // Only log first few events info!("Received market data event for symbol: {}", event.symbol()); } } Err(e) => { error!("Market data event error: {}", e); break; } } } order_event = order_update_rx.recv() => { match order_event { Ok(event) => { info!("Received order update: {:?}", event); } Err(e) => { error!("Order update event error: {}", e); break; } } } } } }).await; if listen_timeout.is_err() { info!("Event listening completed (timeout)"); } // Stop data manager info!("Stopping data manager..."); if let Err(e) = data_manager.stop().await { warn!("Error stopping data manager: {}", e); } else { info!("✓ Data manager stopped successfully"); } } Err(e) => { error!("Failed to start data manager: {}", e); return Err(e.into()); } } Ok(()) } /// Example of order submission (commented out for safety) #[allow(dead_code)] async fn order_submission_example(adapter: &mut InteractiveBrokersAdapter) -> anyhow::Result<()> { info!("=== Order Submission Example (DEMO ONLY) ==="); warn!("This example is for demonstration only - no actual orders will be submitted"); // Create a demo order (will not be submitted) let order = Order { id: OrderId::new(), symbol: Symbol::from("AAPL"), side: OrderSide::Buy, quantity: Quantity::try_from(1.0)?, order_type: OrderType::Limit, price: Some(Price::try_from(100.0)?), // Low price to avoid accidental fills stop_price: None, time_in_force: TimeInForce::Day, reduce_only: false, }; info!("Demo order created:"); info!(" Symbol: {}", order.symbol); info!(" Side: {:?}", order.side); info!(" Quantity: {}", order.quantity); info!(" Type: {:?}", order.order_type); info!(" Price: {:?}", order.price); // In a real application, you would submit the order like this: // let result = adapter.submit_order(order).await; // But for safety, we're just demonstrating the order structure info!("Order submission example completed (no actual order submitted)"); Ok(()) } /// Helper function to check if TWS is running async fn check_tws_status() -> bool { match tokio::net::TcpStream::connect("127.0.0.1:7497").await { Ok(_) => { info!("✓ TWS/IB Gateway appears to be running on port 7497"); true } Err(_) => { warn!("✗ Cannot connect to port 7497 - TWS/IB Gateway may not be running"); warn!("To run this example successfully:"); warn!("1. Install and start TWS or IB Gateway"); warn!("2. Enable API connections in the configuration"); warn!("3. Set the socket port to 7497 (paper trading)"); false } } } /// Configuration helper fn print_setup_instructions() { info!("=== Setup Instructions ==="); info!("To run broker connection examples:"); info!(""); info!("Interactive Brokers:"); info!("1. Download and install TWS or IB Gateway"); info!("2. Start the application and log in"); info!("3. Go to Configure -> API -> Settings"); info!("4. Enable 'Enable ActiveX and Socket Clients'"); info!("5. Set Socket port to 7497 for paper trading"); info!("6. Add 127.0.0.1 to trusted IP addresses"); info!(""); info!("Environment Variables (optional):"); info!("- POLYGON_API_KEY: Your Polygon.io API key"); info!("- IB_TWS_HOST: TWS host (default: 127.0.0.1)"); info!("- IB_TWS_PORT: TWS port (default: 7497)"); info!(""); }