24f96ab78babc1316158fece5326da02002f4e05
Three root-cause bugs made the collapse-recovery distillation mechanism a silent no-op. Diagnosed via 50-epoch smoke test trajectory: Q-gap peaked at 1.2 in epoch 2 then collapsed irreversibly by epoch 6, with HEALTH_DIAG reporting `distill=off` throughout despite the trigger conditions firing every epoch. Fixed each bug in turn and re-ran: Q-gap now stabilizes above 0.18 from epoch 33 onwards, final 1.18. Bug 1: timing apply_distillation_gradient ran at epoch-boundary and SAXPY'd into grad_buf. But the next training step's graph_forward.replay() starts with `cuMemsetD32Async(grad_buf, 0, total_params)` — wiping the contribution before any Adam update could see it. Moved SAXPY into the per-step aux-op phase (between graph_forward and graph_adam), matching the cadence of CQL/IQN/ensemble gradients. Bug 2: CUDA Graph scalar baking Moving SAXPY to per-step hit a deeper issue: graph capture bakes kernel scalar args at capture time. The alpha value was captured at 0.0 (initial) and never updated across replays, regardless of per-epoch recomputation. Fix: new dqn_distill_saxpy_kernel reads health from isv_signals[LEARNING_HEALTH_INDEX=12] directly and computes alpha in-kernel. `distill_best_buf` is a stable device pointer; its contents are DtoD-refreshed at epoch boundary when maybe_snapshot_params accepts a new best. Zero CPU writes on any path — pure GPU dataflow. Bug 3: snapshot gate using wrong signal The snapshot gate passed `self.last_q_gap` (an EMA that was stuck at 0 due to broken propagation — see companion commit). Gate was `health ≥ 0.65 OR winrate_fallback`, neither of which opened in runs where high-q_gap epochs and high-winrate epochs don't overlap. Replaced with q_gap-primary gate: `epoch_q_gap ≥ dynamic_floor`, where the floor is `0.5 × decaying_peak` scaled by a per-epoch 0.99 decay. Adapts to network size automatically (production peaks at ~0.5 → floor 0.25; smoke test peaks at ~0.05 → floor 0.025). Also drops the winrate-fallback "inflate health to 0.75" hack from training_loop — q_gap is the direct measure of what distillation preserves, no proxies needed. Verified: local E1 smoke test (RTX 3050 Ti, 50 epochs) shows distillation engaging from epoch 2 onwards and keeping Q-gap above 0.18 for epochs 33-50. Production L40S 50-epoch run pending deploy. Files changed: - dqn_utility_kernels.cu: new dqn_distill_saxpy_kernel (numerically unchanged from saxpy_f32_kernel; alpha computed from ISV per-thread) - gpu_dqn_trainer.rs: distill_saxpy_aux kernel handle, distill_best_buf stable device buffer initialized from params at construction, apply_distillation_gradient() rewritten, mirror_best_snapshot_to_distill_buf() invoked on snapshot acceptance, maybe_snapshot_params uses dynamic q_gap floor via SnapshotRing::observe_q_gap/dynamic_q_gap_floor - q_snapshot.rs: SnapshotRing grows max_q_gap_observed decaying-peak tracker, observe_q_gap() + dynamic_q_gap_floor() helpers, MIN_SNAPSHOT_Q_GAP constant dropped in favor of relative floor, module docstring rewritten to explain q_gap-primary gate - fused_training.rs: set_distill_alpha + distill_alpha_per_step field dropped (no longer needed — kernel reads ISV directly), submit_aux_ops calls apply_distillation_gradient() unconditionally - training_loop.rs: snapshot call simplified — passes epoch_q_gap (raw, not the stuck EMA), drops winrate fallback and distill alpha plumbing; also calls fused.update_eval_v_range() in the epoch-end Q-stats block (the path previously writing to per_branch_q_gap_ema was disabled via `if false` guard, leaving the health EMA frozen at zero) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foxhunt
Production HFT trading system in Rust.
Architecture
The workspace contains 32 crates organized as follows:
Core Libraries (16)
| Crate | Purpose |
|---|---|
trading_engine |
Order processing, FIX 4.4, IB TWS, SIMD, RDTSC timing |
risk |
VaR, Kelly, circuit breakers, kill switches, compliance |
risk-data |
Risk data types and shared structures |
trading-data |
Trading data types |
ml |
DQN Rainbow, PPO, TFT, Mamba2, ensemble inference |
ml-data |
ML data types and feature definitions |
data |
Market data ingestion and storage |
backtesting |
Replay engine, strategy tester |
adaptive-strategy |
Ensemble execution, microstructure analysis |
common |
Shared types, resilience, error handling |
storage |
S3 and local model storage |
model_loader |
Model serialization and loading |
market-data |
Market data feed handlers |
database |
PostgreSQL access layer (SQLx) |
config |
Configuration management |
tli |
CLI commands and tooling |
Services (8)
| Service | Purpose |
|---|---|
backtesting_service |
gRPC backtesting service |
broker_gateway_service |
FIX routing, broker connectivity |
trading_service |
Core trading operations |
ml_training_service |
Model training orchestration |
data_acquisition_service |
Market data acquisition |
trading_agent_service |
Autonomous trading agents |
api_gateway |
gRPC API gateway with auth |
web-gateway |
Axum REST + WebSocket gateway |
Frontend
web-dashboard/ -- React 19 + TypeScript + Vite + TradingView charts.
Building
# Check compilation (no PostgreSQL required)
SQLX_OFFLINE=true cargo check --workspace
# Run tests for a specific crate
SQLX_OFFLINE=true cargo test -p <crate> --lib
# Clippy
SQLX_OFFLINE=true cargo clippy --workspace
ML Models
Four production model architectures on Candle v0.9.1 with CUDA:
- DQN Rainbow -- Deep Q-Network with prioritized replay, dueling heads, noisy nets
- PPO -- Proximal Policy Optimization with GAE and LSTM policies
- TFT -- Temporal Fusion Transformer for multi-horizon forecasting
- Mamba2 -- State space model for sequence prediction
Each model has a standalone trainer and a UnifiedTrainable adapter for the hyperopt pipeline.
Infrastructure
- Git: Gitea at
git.fxhnt.ai(Tailscale-only), Scaleway DEV1-S - Observability: OpenTelemetry OTLP (env
OTEL_EXPORTER_OTLP_ENDPOINT) - Database: PostgreSQL with SQLx offline mode for CI
License
Proprietary. All rights reserved.
Description
Languages
Rust
88.2%
Cuda
7.7%
Python
1.3%
Shell
1.1%
PLpgSQL
0.8%
Other
0.8%