Files
foxhunt/fxt/docs/USAGE.md
jgrusewski 2da5bafc0e refactor: rename tli→fxt, delete legacy scripts/RunPod/deploy artifacts
- 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>
2026-02-24 10:32:21 +01:00

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

  1. Quick Start
  2. Installation
  3. Basic Usage
  4. Configuration
  5. API Reference
  6. Examples
  7. Testing
  8. Performance
  9. Troubleshooting
  10. 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

  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
// 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:

  1. Check network latency:

    ping trading-engine-host
    traceroute trading-engine-host
    
  2. Monitor resource usage:

    # CPU and memory usage
    top -p $(pgrep your-app)
    
    # Network connections
    ss -tuln | grep :50052
    
  3. 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

Support

For support and questions:

  1. Check the troubleshooting section
  2. Review the 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