- 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>
760 lines
17 KiB
Markdown
760 lines
17 KiB
Markdown
# 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
|
|
|
|
1. [Quick Start](#quick-start)
|
|
2. [Installation](#installation)
|
|
3. [Basic Usage](#basic-usage)
|
|
4. [Configuration](#configuration)
|
|
5. [API Reference](#api-reference)
|
|
6. [Examples](#examples)
|
|
7. [Testing](#testing)
|
|
8. [Performance](#performance)
|
|
9. [Troubleshooting](#troubleshooting)
|
|
10. [Best Practices](#best-practices)
|
|
|
|
## Quick Start
|
|
|
|
```rust
|
|
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`:
|
|
|
|
```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.
|
|
|
|
```rust
|
|
use tli::TliClient;
|
|
|
|
// Default: connects to api_gateway at localhost:50050
|
|
let client = TliClient::new();
|
|
```
|
|
|
|
### Connecting to Services
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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`:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Reuse clients**: Create TLI clients once and reuse them
|
|
2. **Batch operations**: Use streaming for high-frequency data
|
|
3. **Connection pooling**: Share connections across multiple operations
|
|
4. **Async programming**: Use `tokio::spawn` for concurrent operations
|
|
5. **Buffer sizing**: Tune channel buffer sizes for your workload
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```bash
|
|
# Enable debug logs
|
|
export RUST_LOG=debug
|
|
|
|
# Trace-level logging (very verbose)
|
|
export RUST_LOG=trace
|
|
```
|
|
|
|
```rust
|
|
// In code
|
|
tracing_subscriber::fmt()
|
|
.with_max_level(tracing::Level::DEBUG)
|
|
.init();
|
|
```
|
|
|
|
### Performance Issues
|
|
|
|
If experiencing performance issues:
|
|
|
|
1. **Check network latency**:
|
|
```bash
|
|
ping trading-engine-host
|
|
traceroute trading-engine-host
|
|
```
|
|
|
|
2. **Monitor resource usage**:
|
|
```bash
|
|
# CPU and memory usage
|
|
top -p $(pgrep your-app)
|
|
|
|
# Network connections
|
|
ss -tuln | grep :50052
|
|
```
|
|
|
|
3. **Profile your application**:
|
|
```bash
|
|
# Use perf for profiling
|
|
perf record -g cargo run --release --example basic_dashboard
|
|
perf report
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Error Handling
|
|
|
|
Always handle errors appropriately:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
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](https://docs.rs/tli) (when published)
|
|
- [Foxhunt System Documentation](../docs/)
|
|
- [gRPC Documentation](https://grpc.io/docs/)
|
|
- [Tokio Documentation](https://tokio.rs/)
|
|
- [Tracing Documentation](https://tracing.rs/)
|
|
|
|
## Support
|
|
|
|
For support and questions:
|
|
|
|
1. Check the [troubleshooting section](#troubleshooting)
|
|
2. Review the [examples](#examples)
|
|
3. Run the test suite to verify your setup
|
|
4. Check the logs with debug logging enabled
|
|
|
|
## Contributing
|
|
|
|
When contributing to TLI:
|
|
|
|
1. Run the full test suite: `cargo test`
|
|
2. Run benchmarks: `cargo bench`
|
|
3. Check code formatting: `cargo fmt`
|
|
4. Run clippy: `cargo clippy`
|
|
5. Update documentation as needed
|
|
|