//! Complete TLI Client Example //! //! This example demonstrates how to use the TLI client infrastructure //! to connect to both Trading Service and Backtesting Service. use tli::prelude::*; use tokio::time::{sleep, Duration}; use tracing::{error, info, warn}; #[tokio::main] async fn main() -> TliResult<()> { // Initialize logging tracing_subscriber::fmt::init(); info!("Starting TLI Complete Client Example"); // Create client suite with both services let mut client_suite = TliClientBuilder::new() .with_service_endpoint( "trading_service".to_string(), "http://localhost:50051".to_string(), ) .with_service_endpoint( "backtesting_service".to_string(), "http://localhost:50052".to_string(), ) .with_trading_config(TradingClientConfig { service_name: "trading_service".to_string(), request_timeout: Duration::from_secs(10), order_validation: OrderValidationConfig { enable_pre_validation: true, max_order_size: 100_000.0, min_order_size: 1.0, validate_symbols: true, validate_market_hours: true, }, risk_management: RiskManagementConfig { enable_risk_monitoring: true, max_position_exposure: 500_000.0, var_confidence_level: 0.95, alert_thresholds: RiskAlertThresholds::default(), enable_position_limits: true, }, market_data: MarketDataConfig { enable_real_time: true, default_symbols: vec!["SPY".to_string(), "QQQ".to_string(), "AAPL".to_string()], data_types: vec![ MarketDataType::Quotes, MarketDataType::Trades, MarketDataType::Bars, ], buffer_size: 10000, enable_tick_data: false, }, monitoring: MonitoringConfig { enable_monitoring: true, metrics_interval: Duration::from_secs(5), enable_latency_tracking: true, enable_throughput_tracking: true, }, event_streaming: EventStreamConfig { event_types: vec![ EventType::MarketData, EventType::OrderUpdates, EventType::RiskAlerts, EventType::Metrics, EventType::Config, EventType::SystemStatus, ], buffer_size: 1000, reconnect_config: ReconnectConfig::default(), filters: EventFilters::default(), }, }) .with_backtesting_config(BacktestingClientConfig::default()) .build() .await?; info!("Client suite created successfully"); // Demonstrate trading operations if let Some(trading_client) = &client_suite.trading_client { info!("Connecting to trading service..."); // Note: We need to connect first in a real implementation // trading_client.connect().await?; // 1. Get account information info!("Getting account information..."); let account_request = GetAccountInfoRequest { account_id: "demo_account".to_string(), }; match trading_client.get_account_info(account_request).await { Ok(response) => { info!( "Account Info: ID={}, Total Value=${:.2}, Cash=${:.2}, Buying Power=${:.2}", response.account_id, response.total_value, response.cash_balance, response.buying_power ); } Err(e) => warn!("Failed to get account info: {}", e), } // 2. Get current positions info!("Getting current positions..."); let positions_request = GetPositionsRequest { symbol: None }; match trading_client.get_positions(positions_request).await { Ok(response) => { info!("Current positions: {} total", response.positions.len()); for position in response.positions { info!( " {}: {} shares @ ${:.2} (Value: ${:.2}, P&L: ${:.2})", position.symbol, position.quantity, position.market_price, position.market_value, position.unrealized_pnl ); } } Err(e) => warn!("Failed to get positions: {}", e), } // 3. Submit a test order info!("Submitting test order..."); let order_request = SubmitOrderRequest { symbol: "SPY".to_string(), side: OrderSide::Buy.into(), order_type: OrderType::Limit.into(), quantity: 10.0, price: Some(450.0), stop_price: None, time_in_force: "DAY".to_string(), client_order_id: "test_order_001".to_string(), }; match trading_client.submit_order(order_request).await { Ok(response) => { if response.success { info!("Order submitted successfully: {}", response.order_id); } else { warn!("Order submission failed: {}", response.message); } } Err(e) => warn!("Failed to submit order: {}", e), } // 4. Get risk metrics info!("Getting risk metrics..."); let risk_request = GetRiskMetricsRequest { portfolio_id: Some("default".to_string()), start_time_unix_nanos: None, end_time_unix_nanos: None, }; match trading_client.get_risk_metrics(risk_request).await { Ok(response) => { info!( "Risk Metrics: Sharpe={:.2}, Max Drawdown={:.2}%, VaR=${:.2}", response.sharpe_ratio, response.max_drawdown * 100.0, response.value_at_risk ); } Err(e) => warn!("Failed to get risk metrics: {}", e), } // 5. Get system status info!("Getting system status..."); let status_request = GetSystemStatusRequest { service_names: vec![], // Empty for all services }; match trading_client.get_system_status(status_request).await { Ok(response) => { info!( "System Status: {:?} ({} services)", response.overall_status, response.services.len() ); for service in response.services { info!( " {}: {:?} - {}", service.name, service.status, service.message ); } } Err(e) => warn!("Failed to get system status: {}", e), } // 6. Subscribe to market data info!("Subscribing to market data..."); let market_data_request = SubscribeMarketDataRequest { symbols: vec!["SPY".to_string(), "QQQ".to_string()], data_types: vec![MarketDataType::Quotes.into(), MarketDataType::Trades.into()], }; match trading_client .subscribe_market_data(market_data_request) .await { Ok(_) => info!("Successfully subscribed to market data"), Err(e) => warn!("Failed to subscribe to market data: {}", e), } // 7. Get event receiver and process some events if let Some(mut event_receiver) = trading_client.get_event_receiver().await { info!("Processing events for 10 seconds..."); let start = tokio::time::Instant::now(); let mut event_count = 0; while start.elapsed() < Duration::from_secs(10) && event_count < 100 { tokio::select! { result = event_receiver.recv() => { match result { Ok(event) => { event_count += 1; match event { TliEvent::MarketData { event, timestamp, source } => { info!("Market Data event from {}: {:?}", source, event); } TliEvent::OrderUpdate { event, timestamp, source } => { info!("Order Update from {}: Order {} - {:?}", source, event.order_id, event.status); } TliEvent::RiskAlert { event, timestamp, source } => { warn!("Risk Alert from {}: {:?} - {}", source, event.severity, event.message); } TliEvent::Metrics { event, timestamp, source } => { info!("Metrics from {}: {} metrics", source, event.metrics.len()); } TliEvent::Config { event, timestamp, source } => { info!("Config change from {}: {} = {}", source, event.key, event.value); } TliEvent::SystemStatus { event, timestamp, source } => { info!("System status change from {}: {} -> {:?}", source, event.service_name, event.status); } TliEvent::ConnectionStatus { service, connected, timestamp } => { if connected { info!("Service {} connected", service); } else { warn!("Service {} disconnected", service); } } TliEvent::StreamError { event_type, error, timestamp, retryable } => { error!("Stream error for {}: {} (retryable: {})", event_type, error, retryable); } _ => {} } } Err(e) => { warn!("Error receiving event: {}", e); break; } } } _ = sleep(Duration::from_millis(100)) => { // Continue loop } } } info!("Processed {} events", event_count); } // 8. Get client statistics let stats = trading_client.get_stats().await; info!( "Trading Client Stats: {} API calls, {} errors, avg latency: {:?}", stats.api_calls, stats.api_errors, stats.avg_order_latency ); } // Demonstrate backtesting operations if let Some(backtesting_client) = &client_suite.backtesting_client { info!("Connecting to backtesting service..."); // Note: We need to connect first in a real implementation // backtesting_client.connect().await?; // 1. Start a backtest info!("Starting a new backtest..."); let backtest_request = StartBacktestRequest { strategy_name: "mean_reversion_v1".to_string(), symbols: vec!["SPY".to_string(), "QQQ".to_string()], start_date_unix_nanos: 1640995200000000000, // 2022-01-01 end_date_unix_nanos: 1672531200000000000, // 2023-01-01 initial_capital: 100_000.0, parameters: std::collections::HashMap::from([ ("lookback_period".to_string(), "20".to_string()), ("threshold".to_string(), "2.0".to_string()), ]), save_results: true, description: "Testing mean reversion strategy on SPY and QQQ".to_string(), }; match backtesting_client.start_backtest(backtest_request).await { Ok(response) => { if response.success { info!( "Backtest started: {} (estimated duration: {}s)", response.backtest_id, response.estimated_duration_seconds ); // 2. Monitor backtest progress let backtest_id = response.backtest_id.clone(); for i in 0..10 { sleep(Duration::from_secs(2)).await; let status_request = GetBacktestStatusRequest { backtest_id: backtest_id.clone(), }; match backtesting_client.get_backtest_status(status_request).await { Ok(status) => { info!( "Backtest {} progress: {:.1}% - {} trades, P&L: ${:.2}", backtest_id, status.progress_percentage, status.trades_executed, status.current_pnl ); if status.status == BacktestStatus::Completed.into() { info!("Backtest completed!"); break; } else if status.status == BacktestStatus::Failed.into() { error!( "Backtest failed: {}", status.error_message.unwrap_or_default() ); break; } } Err(e) => warn!("Failed to get backtest status: {}", e), } } // 3. Get backtest results info!("Getting backtest results..."); let results_request = GetBacktestResultsRequest { backtest_id: backtest_id.clone(), include_trades: true, include_metrics: true, }; match backtesting_client .get_backtest_results(results_request) .await { Ok(results) => { if let Some(metrics) = &results.metrics { info!("Backtest Results:"); info!(" Total Return: {:.2}%", metrics.total_return * 100.0); info!( " Annualized Return: {:.2}%", metrics.annualized_return * 100.0 ); info!(" Sharpe Ratio: {:.2}", metrics.sharpe_ratio); info!(" Max Drawdown: {:.2}%", metrics.max_drawdown * 100.0); info!(" Win Rate: {:.1}%", metrics.win_rate * 100.0); info!(" Total Trades: {}", metrics.total_trades); info!(" Profit Factor: {:.2}", metrics.profit_factor); } info!("Trade count: {}", results.trades.len()); info!("Equity curve points: {}", results.equity_curve.len()); } Err(e) => warn!("Failed to get backtest results: {}", e), } } else { warn!("Failed to start backtest: {}", response.message); } } Err(e) => warn!("Failed to start backtest: {}", e), } // 4. List historical backtests info!("Listing historical backtests..."); let list_request = ListBacktestsRequest { limit: 10, offset: 0, strategy_name: None, status_filter: None, }; match backtesting_client.list_backtests(list_request).await { Ok(response) => { info!( "Found {} backtests (total: {})", response.backtests.len(), response.total_count ); for backtest in response.backtests { info!( " {}: {} - Return: {:.2}%, Sharpe: {:.2}, MaxDD: {:.2}%", backtest.backtest_id, backtest.strategy_name, backtest.total_return * 100.0, backtest.sharpe_ratio, backtest.max_drawdown * 100.0 ); } } Err(e) => warn!("Failed to list backtests: {}", e), } } // Get connection statistics info!("Getting connection statistics..."); let connection_stats = client_suite.get_connection_stats().await; for (service, stats_list) in connection_stats { info!("Service {}: {} connections", service, stats_list.len()); for stats in stats_list { info!( " Connection: {} requests, {} errors, latency: {:?}", stats.requests_sent, stats.errors, stats.average_latency ); } } // Shutdown info!("Shutting down client suite..."); client_suite.shutdown().await; info!("TLI Complete Client Example finished"); Ok(()) }