- Rename tli/ directory to fxt/, update package + binary name to "fxt" - Replace all `use tli::` → `use fxt::` across 52 Rust files - Update build.rs proto paths (tli/proto → fxt/proto) in 6 services - Update Dockerfiles, CI workflows, deploy.sh for new paths - Delete ~170 legacy shell scripts (kept 15 essential ones) - Delete RunPod Python client (runpod/), tests (tests/runpod/) - Delete foxhunt-deploy crate (RunPod-only deployment tool) - Delete terraform/runpod/ (moved to Scaleway) - Delete ML Python hyperopt scripts (replaced by Rust Argmin PSO) - Delete .gitlab-ci.yml (using GitHub + Gitea) - Remove foxhunt-deploy from workspace members 504 files changed, -74,355 lines of legacy code removed. Workspace compiles clean (0 errors, 0 warnings). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
TLI (Terminal Line Interface) Usage Guide
Welcome to the Foxhunt TLI usage guide. This document provides comprehensive information on how to use the TLI client library to interact with the Foxhunt High-Frequency Trading system.
Table of Contents
- Quick Start
- Installation
- Basic Usage
- Configuration
- API Reference
- Examples
- Testing
- Performance
- Troubleshooting
- Best Practices
Quick Start
use tli::prelude::*;
use tli::TliClient;
#[tokio::main]
async fn main() -> TliResult<()> {
// Create and connect to TLI client
let mut client = TliClient::new();
client.connect().await?;
// Check system health
let health = client.check_health().await?;
println!("System status: {:?}", health);
// Disconnect when done
client.disconnect().await;
Ok(())
}
Installation
Add TLI to your Cargo.toml:
[dependencies]
tli = { path = "../tli" } # Local development
# or when published:
# tli = "0.1.0"
# Required async runtime
tokio = { version = "1.0", features = ["full"] }
# For logging (recommended)
tracing = "0.1"
tracing-subscriber = "0.3"
Basic Usage
Creating a Client
All traffic routes through the api_gateway at port 50050.
use tli::TliClient;
// Default: connects to api_gateway at localhost:50050
let client = TliClient::new();
Connecting to Services
let mut client = TliClient::new();
// Connect to all services
client.connect().await?;
// Check connection status
let health_status = client.check_health().await?;
for status in health_status {
println!("{}: {} ({})", status.service, status.status, status.endpoint);
}
Order Management
use tli::proto::trading::*;
// Submit an order
let order_request = SubmitOrderRequest {
symbol: "AAPL".to_string(),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: Some(150.0),
};
let trading_client = client.trading()?;
let response = trading_client
.submit_order(tonic::Request::new(order_request))
.await?;
println!("Order submitted: {}", response.into_inner().order_id);
// List orders
let list_request = ListOrdersRequest {
symbol: "".to_string(), // All symbols
limit: Some(10),
};
let response = trading_client
.list_orders(tonic::Request::new(list_request))
.await?;
for order in response.into_inner().orders {
println!("Order: {} - {} {} @ ${}",
order.order_id, order.symbol, order.quantity, order.price);
}
Real-time Streaming
use tokio_stream::StreamExt;
// Start order updates stream
let request = StreamOrderUpdatesRequest {};
let response = trading_client
.stream_order_updates(tonic::Request::new(request))
.await?;
let mut stream = response.into_inner();
// Process updates
while let Some(update) = stream.next().await {
match update {
Ok(order_update) => {
println!("Order update: {} - Status: {}",
order_update.order_id, order_update.status);
}
Err(e) => {
eprintln!("Stream error: {}", e);
break;
}
}
}
Configuration Management
// Get configuration
let config_client = client.config()?;
let get_request = GetConfigRequest {
key: "max_order_size".to_string(),
};
let response = config_client
.get_config(tonic::Request::new(get_request))
.await?;
if let Some(config) = response.into_inner().config {
println!("Config: {} = {}", config.key, config.value);
}
// Set configuration
let set_request = SetConfigRequest {
key: "trading_enabled".to_string(),
value: "true".to_string(),
config_type: "boolean".to_string(),
description: "Enable trading operations".to_string(),
};
let response = config_client
.set_config(tonic::Request::new(set_request))
.await?;
println!("Config updated: {}", response.into_inner().message);
Monitoring and Metrics
// Get system status
let monitoring_client = client.monitoring()?;
let status_request = GetSystemStatusRequest {};
let response = monitoring_client
.get_system_status(tonic::Request::new(status_request))
.await?;
for service in response.into_inner().services {
println!("Service: {} - Status: {}", service.name, service.status);
}
// Get metrics
let metrics_request = GetMetricsRequest {
metric_names: vec![
"latency_p99".to_string(),
"orders_per_second".to_string(),
],
};
let response = monitoring_client
.get_metrics(tonic::Request::new(metrics_request))
.await?;
for metric in response.into_inner().metrics {
println!("Metric: {} = {} {}", metric.name, metric.value, metric.unit);
}
Configuration
Environment Variables
TLI can be configured using environment variables:
# API Gateway endpoint (all services route through here)
export FOXHUNT_API_GATEWAY_URL="http://localhost:50050"
# Logging
export RUST_LOG="info"
# Testing
export TLI_ENABLE_RT_TESTS="true"
Connection Timeouts
use std::time::Duration;
use tonic::transport::Endpoint;
// Custom timeout configuration
let channel = Endpoint::from_shared("http://localhost:50050")?
.timeout(Duration::from_secs(10))
.connect_timeout(Duration::from_secs(5))
.connect()
.await?;
API Reference
Core Types
// Re-exported from the prelude
use tli::prelude::*;
// Main client
pub struct TliClient { /* ... */ }
// Error types
pub enum TliError {
Connection(String),
InvalidRequest(String),
InvalidSymbol(String),
NotConnected(String),
}
pub type TliResult<T> = Result<T, TliError>;
Client Methods
impl TliClient {
// Creation
pub fn new() -> Self;
// Connection management
pub async fn connect(&mut self) -> TliResult<()>;
pub async fn disconnect(&mut self);
pub async fn check_health(&mut self) -> TliResult<Vec<ServiceHealth>>;
// Service access
pub fn trading(&mut self) -> TliResult<&mut TradingServiceClient<Channel>>;
pub fn monitoring(&mut self) -> TliResult<&mut MonitoringServiceClient<Channel>>;
pub fn config(&mut self) -> TliResult<&mut ConfigServiceClient<Channel>>;
}
Utility Functions
// Timestamp conversions
pub fn unix_nanos_to_system_time(nanos: i64) -> SystemTime;
pub fn system_time_to_unix_nanos(time: SystemTime) -> i64;
pub fn current_unix_nanos() -> i64;
// Validation
pub fn validate_symbol(symbol: &str) -> TliResult<()>;
pub fn validate_quantity(quantity: f64) -> TliResult<()>;
pub fn validate_price(price: f64) -> TliResult<()>;
// Type conversions
pub fn order_side_to_string(side: OrderSide) -> &'static str;
pub fn string_to_order_side(side: &str) -> TliResult<OrderSide>;
pub fn order_type_to_string(order_type: OrderType) -> &'static str;
pub fn string_to_order_type(order_type: &str) -> TliResult<OrderType>;
Examples
Configuration Management
Run the configuration management example:
# Basic configuration operations
cargo run --example config_management basic
# Configuration validation demo
cargo run --example config_management validation
# Search and filtering
cargo run --example config_management search
# Interactive editor
cargo run --example config_management interactive
Features:
- Configuration CRUD operations
- Validation with custom rules
- Category-based organization
- Search and filtering
- Sensitive data masking
Real-time Streaming
Run the streaming example:
# Basic streaming (requires running services)
cargo run --example real_time_streaming basic
# High-frequency data handling
cargo run --example real_time_streaming highfreq
# Error handling and recovery
cargo run --example real_time_streaming errors
# Backpressure handling
cargo run --example real_time_streaming backpressure
Features:
- Multiple concurrent streams
- High-frequency data processing
- Error handling and recovery
- Backpressure management
- Real-time aggregation
Testing
Running Tests
# Unit tests
cargo test
# Integration tests
cargo test --test integration
# Property-based tests
cargo test --features proptest
# With real-time tests (requires services)
TLI_ENABLE_RT_TESTS=true cargo test
Mock Server Testing
# Start mock servers for testing
cargo test --test integration -- --nocapture
# Test with specific mock port
TEST_MOCK_PORT=51000 cargo test
Property-Based Testing
The TLI library includes property-based tests using proptest:
proptest! {
#[test]
fn test_timestamp_conversion_property(timestamp in 0i64..i64::MAX/2) {
let system_time = unix_nanos_to_system_time(timestamp);
let converted = system_time_to_unix_nanos(system_time);
prop_assert!((converted - timestamp).abs() < 1000);
}
}
Performance
Benchmarking
Run performance benchmarks:
# All benchmarks
cargo bench
# Specific benchmark suites
cargo bench --bench configuration_benchmarks
cargo bench --bench client_performance
cargo bench --bench serialization_benchmarks
# With HTML reports
cargo bench -- --output-format html
Performance Characteristics
Based on benchmarks, TLI provides:
- Timestamp conversions: < 100ns per operation
- Validation operations: < 50ns per validation
- Type conversions: < 10ns per conversion
- Protobuf serialization: ~1-10μs depending on message size
- JSON serialization: ~5-50μs depending on message size
Optimization Tips
- Reuse clients: Create TLI clients once and reuse them
- Batch operations: Use streaming for high-frequency data
- Connection pooling: Share connections across multiple operations
- Async programming: Use
tokio::spawnfor concurrent operations - Buffer sizing: Tune channel buffer sizes for your workload
// Good: Reuse client
let mut client = TliClient::new();
client.connect().await?;
for _ in 0..1000 {
let _ = client.check_health().await?;
}
// Better: Batch operations
let health_checks = (0..1000).map(|_| client.check_health());
let results = futures::future::join_all(health_checks).await;
Troubleshooting
Common Issues
Connection Refused
Error: Connection(Connection refused)
Solution: Ensure the api_gateway is running and accessible:
# Check if api_gateway is running
netstat -ln | grep 50050
# Check environment variables
echo $FOXHUNT_API_GATEWAY_URL
Service Not Connected
Error: NotConnected(Trading service not connected)
Solution: Call connect() before using services:
let mut client = TliClient::new();
client.connect().await?; // Required before using services
let trading_client = client.trading()?;
Invalid Symbol
Error: InvalidSymbol(Symbol contains invalid characters)
Solution: Use valid symbol format:
// Valid symbols
validate_symbol("AAPL")?; // ✓
validate_symbol("BTC.USD")?; // ✓
validate_symbol("EUR-USD")?; // ✓
// Invalid symbols
validate_symbol("BTC/USD")?; // ✗ - slash not allowed
validate_symbol("")?; // ✗ - empty not allowed
Timeout Errors
Error: Connection(Request timeout)
Solution: Increase timeouts or check network connectivity:
// Custom timeout configuration via tonic Endpoint
use tonic::transport::Endpoint;
use std::time::Duration;
let channel = Endpoint::from_shared("http://localhost:50050")?
.timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))
.connect()
.await?;
Debug Logging
Enable debug logging for troubleshooting:
# Enable debug logs
export RUST_LOG=debug
# Trace-level logging (very verbose)
export RUST_LOG=trace
// In code
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
Performance Issues
If experiencing performance issues:
-
Check network latency:
ping trading-engine-host traceroute trading-engine-host -
Monitor resource usage:
# CPU and memory usage top -p $(pgrep your-app) # Network connections ss -tuln | grep :50052 -
Profile your application:
# Use perf for profiling perf record -g cargo run --release --example basic_dashboard perf report
Best Practices
Error Handling
Always handle errors appropriately:
// Good: Proper error handling
match client.check_health().await {
Ok(health) => {
// Process health status
for status in health {
if status.status.contains("ERROR") {
warn!("Service {} has issues: {}", status.service, status.status);
}
}
}
Err(TliError::Connection(msg)) => {
error!("Connection failed: {}", msg);
// Implement retry logic
}
Err(TliError::NotConnected(msg)) => {
warn!("Not connected: {}", msg);
// Attempt reconnection
}
Err(e) => {
error!("Unexpected error: {}", e);
}
}
Resource Management
Properly manage connections and resources:
// Good: Explicit disconnection
{
let mut client = TliClient::new();
client.connect().await?;
// Use client...
client.disconnect().await; // Explicit cleanup
}
// Better: Use RAII pattern
struct TradingSession {
client: TliClient,
}
impl TradingSession {
async fn new() -> TliResult<Self> {
let mut client = TliClient::new();
client.connect().await?;
Ok(Self { client })
}
}
impl Drop for TradingSession {
fn drop(&mut self) {
// Note: async drop not available, use explicit cleanup
// or spawn a task for async cleanup
}
}
Concurrent Operations
Use async properly for concurrent operations:
// Good: Concurrent health checks for multiple clients
let clients = vec![client1, client2, client3];
let health_checks = clients.iter_mut()
.map(|client| client.check_health());
let results = futures::future::join_all(health_checks).await;
// Better: Use spawn for truly parallel operations
let tasks: Vec<_> = clients.into_iter()
.map(|mut client| tokio::spawn(async move {
client.check_health().await
}))
.collect();
let results = futures::future::join_all(tasks).await;
Validation
Always validate input data:
// Validate before submitting orders
fn validate_order_request(request: &SubmitOrderRequest) -> TliResult<()> {
validate_symbol(&request.symbol)?;
validate_quantity(request.quantity)?;
if let Some(price) = request.price {
validate_price(price)?;
}
Ok(())
}
// Use in order submission
validate_order_request(&order_request)?;
let response = trading_client.submit_order(tonic::Request::new(order_request)).await?;
Configuration Management
Use environment-specific configuration:
// Environment-aware configuration
fn get_endpoints_for_env() -> ServiceEndpoints {
match std::env::var("ENVIRONMENT").as_deref() {
Ok("production") => ServiceEndpoints {
trading_engine: "https://prod-trading.company.com".to_string(),
// ... production endpoints
},
Ok("staging") => ServiceEndpoints {
trading_engine: "https://staging-trading.company.com".to_string(),
// ... staging endpoints
},
_ => ServiceEndpoints::default(), // Development defaults
}
}
Monitoring and Observability
Implement proper monitoring:
use tracing::{info, warn, error, instrument};
#[instrument]
async fn submit_order_with_monitoring(
client: &mut TliClient,
request: SubmitOrderRequest,
) -> TliResult<String> {
let start = std::time::Instant::now();
match client.trading()?.submit_order(tonic::Request::new(request)).await {
Ok(response) => {
let elapsed = start.elapsed();
info!("Order submitted successfully in {:?}", elapsed);
Ok(response.into_inner().order_id)
}
Err(e) => {
let elapsed = start.elapsed();
error!("Order submission failed after {:?}: {}", elapsed, e);
Err(TliError::Connection(e.to_string()))
}
}
}
Additional Resources
- TLI API Documentation (when published)
- Foxhunt System Documentation
- gRPC Documentation
- Tokio Documentation
- Tracing Documentation
Support
For support and questions:
- Check the troubleshooting section
- Review the examples
- Run the test suite to verify your setup
- Check the logs with debug logging enabled
Contributing
When contributing to TLI:
- Run the full test suite:
cargo test - Run benchmarks:
cargo bench - Check code formatting:
cargo fmt - Run clippy:
cargo clippy - Update documentation as needed