#!/usr/bin/env python3 """ Performance validation script for Foxhunt HFT Trading System Analyzes benchmark results and validates against HFT latency requirements """ import json import re import sys import statistics from pathlib import Path from typing import Dict, List, Tuple, Optional from dataclasses import dataclass from datetime import datetime @dataclass class PerformanceMetrics: """Performance metrics extracted from benchmark results""" name: str mean_ns: float median_ns: float p95_ns: float p99_ns: float std_dev_ns: float throughput_ops_sec: Optional[float] = None @dataclass class PerformanceThresholds: """HFT performance thresholds for validation""" max_latency_us: float max_p95_latency_us: float max_p99_latency_us: float min_throughput_ops_sec: float max_std_dev_us: float # HFT performance requirements PERFORMANCE_THRESHOLDS = { 'trading_latency': PerformanceThresholds( max_latency_us=30.0, max_p95_latency_us=50.0, max_p99_latency_us=100.0, min_throughput_ops_sec=100_000, max_std_dev_us=10.0 ), 'order_processing': PerformanceThresholds( max_latency_us=25.0, max_p95_latency_us=40.0, max_p99_latency_us=80.0, min_throughput_ops_sec=150_000, max_std_dev_us=8.0 ), 'ml_inference': PerformanceThresholds( max_latency_us=50.0, max_p95_latency_us=100.0, max_p99_latency_us=200.0, min_throughput_ops_sec=50_000, max_std_dev_us=20.0 ), 'risk_calculations': PerformanceThresholds( max_latency_us=20.0, max_p95_latency_us=35.0, max_p99_latency_us=70.0, min_throughput_ops_sec=200_000, max_std_dev_us=5.0 ), } class PerformanceValidator: """Validates benchmark results against HFT performance requirements""" def __init__(self, benchmark_file: str): self.benchmark_file = Path(benchmark_file) self.results: List[PerformanceMetrics] = [] self.validation_errors: List[str] = [] self.validation_warnings: List[str] = [] def parse_criterion_output(self, content: str) -> List[PerformanceMetrics]: """Parse Criterion benchmark output""" metrics = [] # Pattern for Criterion benchmark results benchmark_pattern = r'(\w+)\s+time:\s+\[([0-9.]+)\s+([μnm]?s)\s+([0-9.]+)\s+([μnm]?s)\s+([0-9.]+)\s+([μnm]?s)\]' throughput_pattern = r'(\w+)\s+throughput:\s+\[([0-9.]+)\s+([KMG]?ops/s)\s+([0-9.]+)\s+([KMG]?ops/s)\s+([0-9.]+)\s+([KMG]?ops/s)\]' for match in re.finditer(benchmark_pattern, content): name = match.group(1) # Convert times to nanoseconds mean_val, mean_unit = float(match.group(2)), match.group(3) median_val, median_unit = float(match.group(4)), match.group(5) p95_val, p95_unit = float(match.group(6)), match.group(7) mean_ns = self._convert_to_nanoseconds(mean_val, mean_unit) median_ns = self._convert_to_nanoseconds(median_val, median_unit) p95_ns = self._convert_to_nanoseconds(p95_val, p95_unit) # Estimate P99 (usually ~1.5x P95 for typical distributions) p99_ns = p95_ns * 1.5 # Estimate standard deviation (rough approximation) std_dev_ns = (p95_ns - mean_ns) / 1.645 # Assuming normal distribution metrics.append(PerformanceMetrics( name=name, mean_ns=mean_ns, median_ns=median_ns, p95_ns=p95_ns, p99_ns=p99_ns, std_dev_ns=std_dev_ns )) # Parse throughput information for match in re.finditer(throughput_pattern, content): name = match.group(1) throughput_val = float(match.group(4)) # Use median throughput throughput_unit = match.group(5) # Convert to ops/sec throughput_ops_sec = self._convert_to_ops_per_second(throughput_val, throughput_unit) # Find corresponding metrics entry for metric in metrics: if metric.name == name: metric.throughput_ops_sec = throughput_ops_sec break return metrics def parse_benchmark_json(self, content: str) -> List[PerformanceMetrics]: """Parse JSON benchmark results""" try: data = json.loads(content) metrics = [] for benchmark in data.get('benchmarks', []): name = benchmark.get('name', 'unknown') # Extract timing statistics stats = benchmark.get('stats', {}) mean_ns = stats.get('mean', 0) * 1e9 # Convert to nanoseconds median_ns = stats.get('median', 0) * 1e9 p95_ns = stats.get('p95', 0) * 1e9 p99_ns = stats.get('p99', mean_ns * 2) # Fallback if not available std_dev_ns = stats.get('std_dev', 0) * 1e9 throughput_ops_sec = benchmark.get('throughput_ops_sec') metrics.append(PerformanceMetrics( name=name, mean_ns=mean_ns, median_ns=median_ns, p95_ns=p95_ns, p99_ns=p99_ns, std_dev_ns=std_dev_ns, throughput_ops_sec=throughput_ops_sec )) return metrics except json.JSONDecodeError as e: print(f"Error parsing JSON benchmark results: {e}") return [] def _convert_to_nanoseconds(self, value: float, unit: str) -> float: """Convert time value to nanoseconds""" unit_multipliers = { 'ns': 1, 'μs': 1_000, 'us': 1_000, # Alternative microsecond notation 'ms': 1_000_000, 's': 1_000_000_000 } return value * unit_multipliers.get(unit, 1) def _convert_to_ops_per_second(self, value: float, unit: str) -> float: """Convert throughput to operations per second""" unit_multipliers = { 'ops/s': 1, 'Kops/s': 1_000, 'Mops/s': 1_000_000, 'Gops/s': 1_000_000_000 } return value * unit_multipliers.get(unit, 1) def load_benchmark_results(self) -> bool: """Load and parse benchmark results""" if not self.benchmark_file.exists(): self.validation_errors.append(f"Benchmark file not found: {self.benchmark_file}") return False try: content = self.benchmark_file.read_text() # Try JSON format first if content.strip().startswith('{'): self.results = self.parse_benchmark_json(content) else: # Fall back to Criterion text output self.results = self.parse_criterion_output(content) if not self.results: self.validation_errors.append("No benchmark results found in file") return False return True except Exception as e: self.validation_errors.append(f"Error reading benchmark file: {e}") return False def validate_performance(self) -> bool: """Validate performance metrics against thresholds""" validation_passed = True for metric in self.results: # Find matching threshold threshold = None for threshold_name, threshold_config in PERFORMANCE_THRESHOLDS.items(): if threshold_name in metric.name.lower(): threshold = threshold_config break if not threshold: self.validation_warnings.append(f"No threshold defined for benchmark: {metric.name}") continue # Validate latency mean_us = metric.mean_ns / 1_000 p95_us = metric.p95_ns / 1_000 p99_us = metric.p99_ns / 1_000 std_dev_us = metric.std_dev_ns / 1_000 if mean_us > threshold.max_latency_us: self.validation_errors.append( f"{metric.name}: Mean latency {mean_us:.2f}μs exceeds threshold {threshold.max_latency_us}μs" ) validation_passed = False if p95_us > threshold.max_p95_latency_us: self.validation_errors.append( f"{metric.name}: P95 latency {p95_us:.2f}μs exceeds threshold {threshold.max_p95_latency_us}μs" ) validation_passed = False if p99_us > threshold.max_p99_latency_us: self.validation_errors.append( f"{metric.name}: P99 latency {p99_us:.2f}μs exceeds threshold {threshold.max_p99_latency_us}μs" ) validation_passed = False if std_dev_us > threshold.max_std_dev_us: self.validation_warnings.append( f"{metric.name}: High latency variance {std_dev_us:.2f}μs (threshold: {threshold.max_std_dev_us}μs)" ) # Validate throughput if available if metric.throughput_ops_sec and metric.throughput_ops_sec < threshold.min_throughput_ops_sec: self.validation_errors.append( f"{metric.name}: Throughput {metric.throughput_ops_sec:.0f} ops/sec below threshold {threshold.min_throughput_ops_sec} ops/sec" ) validation_passed = False return validation_passed def generate_report(self) -> str: """Generate performance validation report""" report = [] report.append("# Foxhunt HFT Performance Validation Report") report.append(f"Generated: {datetime.now().isoformat()}") report.append(f"Benchmark file: {self.benchmark_file}") report.append("") # Summary total_benchmarks = len(self.results) errors_count = len(self.validation_errors) warnings_count = len(self.validation_warnings) if errors_count == 0: report.append("## ✅ VALIDATION PASSED") else: report.append("## ❌ VALIDATION FAILED") report.append(f"- Total benchmarks: {total_benchmarks}") report.append(f"- Validation errors: {errors_count}") report.append(f"- Validation warnings: {warnings_count}") report.append("") # Detailed results report.append("## Performance Metrics") report.append("") for metric in self.results: report.append(f"### {metric.name}") report.append(f"- Mean latency: {metric.mean_ns/1000:.2f}μs") report.append(f"- Median latency: {metric.median_ns/1000:.2f}μs") report.append(f"- P95 latency: {metric.p95_ns/1000:.2f}μs") report.append(f"- P99 latency: {metric.p99_ns/1000:.2f}μs") report.append(f"- Standard deviation: {metric.std_dev_ns/1000:.2f}μs") if metric.throughput_ops_sec: report.append(f"- Throughput: {metric.throughput_ops_sec:,.0f} ops/sec") report.append("") # Errors and warnings if self.validation_errors: report.append("## ❌ Validation Errors") for error in self.validation_errors: report.append(f"- {error}") report.append("") if self.validation_warnings: report.append("## ⚠️ Validation Warnings") for warning in self.validation_warnings: report.append(f"- {warning}") report.append("") # Thresholds reference report.append("## Performance Thresholds") report.append("") for name, threshold in PERFORMANCE_THRESHOLDS.items(): report.append(f"### {name}") report.append(f"- Max mean latency: {threshold.max_latency_us}μs") report.append(f"- Max P95 latency: {threshold.max_p95_latency_us}μs") report.append(f"- Max P99 latency: {threshold.max_p99_latency_us}μs") report.append(f"- Min throughput: {threshold.min_throughput_ops_sec:,} ops/sec") report.append(f"- Max std deviation: {threshold.max_std_dev_us}μs") report.append("") return "\n".join(report) def main(): if len(sys.argv) != 2: print("Usage: python3 validate-performance.py ") sys.exit(1) benchmark_file = sys.argv[1] validator = PerformanceValidator(benchmark_file) # Load benchmark results if not validator.load_benchmark_results(): print("Failed to load benchmark results:") for error in validator.validation_errors: print(f" - {error}") sys.exit(1) # Validate performance validation_passed = validator.validate_performance() # Generate and save report report = validator.generate_report() # Write report to file report_file = Path("performance-validation-report.md") report_file.write_text(report) # Print summary print(f"Performance validation {'PASSED' if validation_passed else 'FAILED'}") print(f"Report saved to: {report_file}") # Print errors to stderr if validator.validation_errors: print("\nValidation errors:") for error in validator.validation_errors: print(f" - {error}", file=sys.stderr) if validator.validation_warnings: print("\nValidation warnings:") for warning in validator.validation_warnings: print(f" - {warning}") # Exit with appropriate code sys.exit(0 if validation_passed else 1) if __name__ == "__main__": main()