Commit Graph

2782 Commits

Author SHA1 Message Date
jgrusewski
921c9309e9 fix(ml): address final review findings — rename, mutability, bounds
- Rename SlippageModel trait → MarketImpactModel to avoid name collision
  with PPO's SlippageModel enum
- Wrap ThompsonSamplingState in std::sync::Mutex for interior mutability
  through Arc, enabling record_outcome during inference
- Add TrafficSplitter::record_outcome() method for callers
- Cap RegimeDetectionEngine::feature_data to window_size*3 entries
- Add trailing newline to ab_testing.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 05:23:31 +01:00
jgrusewski
30dbce211a feat(ml): add Thompson Sampling and Bayesian promotion to A/B testing
Extends TrafficSplitter with Thompson Sampling (Beta-distributed traffic
allocation) and PromotionCriteria for automated champion/challenger
decisions. 18 tests, 0 clippy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 05:09:12 +01:00
jgrusewski
845f8707b9 feat(ml): add online learning with EWC regularization
Per-trade online learning with Elastic Weight Consolidation to prevent
catastrophic forgetting. Rolling 10K experience buffer, mini-updates
every 100 trades. Safety rails: grad clip 1.0, LR×0.1, auto-rollback
at 20% Sharpe degradation, kill switch at Sharpe < -1.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 04:25:14 +01:00
jgrusewski
c2e31e2c40 feat(ml): add multi-timeframe feature fusion (default on, 57→185 dims)
BarResampler aggregates 1m OHLCV to 5m/15m/1h on-the-fly. Four LSTM
encoders (6→64 each) concat to 256, project to 128-dim macro context.
Combined state: 57 position-aware + 128 multi-tf = 185 dims.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:42:50 +01:00
jgrusewski
3014c5a2a3 feat(ml): add curriculum learning with 3-phase schedule
3-phase curriculum: Basic (27 actions, Ranging only) → FullPosition
(45 actions, +Trending) → AllRegimes. Phase gates: Sharpe >0.5/3 folds,
>0.3/2 folds. Composes with position-limit masking via element-wise AND.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:22:55 +01:00
jgrusewski
63bb4e98af feat(ml): add volume-dependent slippage model
SlippageModel trait with two implementations:
- LinearImpactModel: sqrt market impact (spread/2 + eta*sqrt(V/ADV) + order premium)
- FixedCostModel: backward-compat wrapper for existing 15/5/10 bps constants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 03:07:45 +01:00
jgrusewski
a4fc3cdd33 feat(ml): real regime detection with feature classifier + HMM
Replace stub RegimeDetectionEngine (hardcoded "normal") with real
implementations:

- FeatureClassifier: real-time classification using ADX, Hurst exponent,
  and volatility z-score thresholds (Trending/Ranging/Volatile)
- HiddenMarkovModel: 3-state Gaussian HMM with Baum-Welch EM fitting
  and K-means initialization for offline regime transition estimation
- RegimeDetectionEngine: wired to FeatureClassifier, returns RegimeType
  instead of String, accumulates ADX/Hurst/vol from update_features()

26 tests (9 feature_classifier + 6 HMM + 7 engine + 4 existing), 0 clippy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:48:56 +01:00
jgrusewski
96823e4d90 feat(ml): add position-aware feature extraction (PositionFeatures)
Add PositionFeatures struct that extracts 3 features for RL agent
position awareness (54→57 dim state vector):
- unrealized_pnl: normalized by EMA volatility
- bars_in_position: log-scaled to [0, 1]
- cost_basis: relative to price in bps, clamped [-500, 500]

8 unit tests, 0 clippy warnings. Not yet wired into trainers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:38:21 +01:00
jgrusewski
866da8bbbb docs: RL agent gaps implementation plan — 7 tasks, vertical slices
Position-aware state, regime detection, slippage, curriculum learning,
multi-timeframe fusion, online learning with EWC, A/B testing extension.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:30:00 +01:00
jgrusewski
44b38e8014 docs: RL agent gaps design — 7 features for training quality and production readiness
Position-aware state encoding, regime detection (feature-based + HMM),
slippage modeling, curriculum learning, multi-timeframe fusion (default on),
online learning with EWC, and A/B testing with Thompson Sampling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:23:38 +01:00
jgrusewski
0840f8a971 Merge branch 'worktree-dqn-action-collapse-fix' 2026-03-03 02:04:57 +01:00
jgrusewski
864cf8690d fix(dqn,ppo): prevent 45-action collapse with entropy, exploration, and monitoring fixes
DQN: wire SAC-style entropy regularizer into TD loss, fix diversity
penalty normalization from ln(3) to ln(45), add epsilon floor (0.05)
for noisy nets, enable sigma scheduling (0.8→0.4 with 0.3 floor),
add UCB count-based exploration bonus, expand hyperopt search space
from 43D to 45D.

PPO: replace fabricated entropy metric (value_loss×0.5) with real
Shannon entropy from action distribution, fix EntropyRegularizer
normalization from ln(3) to ln(45), implement Monte Carlo entropy
estimate for flow policy (was returning zeros), fix hyperopt VRAM
bound from 3 to 45 actions.

Monitoring: add normalized action entropy and diversity Prometheus
gauges, add exploration diagnostics logging per epoch.

2503 ml tests pass, 277 common tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:04:22 +01:00
jgrusewski
eed268c230 chore: update Cargo.lock and regenerated e2e proto
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
cadea0733f feat(ml_training_service): add ApproveModel/RejectModel handlers + proxy
- ml_training_service: handlers delegate to promotion_manager
- api_gateway: proxy pass-through for both new RPCs
- Proto messages added to service-side ml_training.proto

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
a5ca926af6 feat(fxt): wire all stub commands to real gRPC backends
- model list: calls MLTrainingService::ListAvailableModels
- model approve: calls MLTrainingService::ApproveModel
- model reject: calls MLTrainingService::RejectModel
- agent allocate-portfolio: replaces mock assets with GetSelectedAssets RPC
- auth login (non-interactive): real gRPC via LoginClient::login_with_credentials
- tune --watch: uncommented tune_stream module, wired into tune command

177 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
0a15058990 feat(fxt): implement fxt watch streaming dashboard
- streams.rs: gRPC stream manager with reconnect + exponential backoff
  (training metrics, order updates, risk alerts, system status)
- render.rs: ratatui renderer for 4 tabs (Training/Trading/Risk/System)
- event_loop.rs: tokio::select! loop over gRPC streams, crossterm keys, 200ms tick
- Wired Watch command into CLI (main.rs Commands enum + dispatch)
- 35 tests pass, 0 clippy warnings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
d4d070b70b feat(fxt): add dashboard state types with 5 tests
DashboardState with 4 tab substates (Training/Trading/Risk/System),
ring buffers for loss history and fills, scroll support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
e1bab1a587 feat(fxt): add ratatui/crossterm deps and ApproveModel/RejectModel proto RPCs
- ratatui 0.29, crossterm 0.28 (event-stream), tokio-stream to deps
- ApproveModel + RejectModel RPCs added to MLTrainingService
- 4 new proto messages for model promotion workflow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:02:20 +01:00
jgrusewski
e9c0edd298 docs: fxt dashboard implementation plan (15 tasks)
Bite-sized TDD tasks: ratatui deps, proto RPCs, state types, stream
manager, renderer, event loop, CLI wiring, 6 stub fixes, clippy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:20:27 +01:00
jgrusewski
5b328750a4 docs: fxt dashboard & CLI completion design
Ratatui streaming dashboard (fxt watch) with 4 tabs, stub command
wiring plan, and new ApproveModel/RejectModel proto RPCs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:14:02 +01:00
jgrusewski
97f69111c5 docs: DQN action collapse fix design — 5 fixes for 45-action diversity
Root causes: entropy regularizer dead code, noisy nets disable epsilon,
diversity penalty calibrated for 3 actions not 45.

Fixes: wire entropy into loss, fix normalization, epsilon floor for
noisy nets, sigma scheduling, UCB count-based bonus.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:59:20 +01:00
jgrusewski
410ede2ad7 fix(ml): OOM guard on CPU PER fallback, clippy cleanup, PPO recurrent test fixes
- Add 4 GB pre-flight memory check in try_gpu_prioritized_with_fallback
  before attempting CPU PER fallback (prevents system OOM on absurd capacity)
- GPU pre-flight already rejects oversized buffers; CPU fallback was unguarded
- Fix integration test: assert Err for 500M capacity instead of unwrap
- Fix PPO recurrent tests: TradingAction → FactoredAction, num_actions 3/5 → 45
- Clippy: wildcard match arms → explicit variants, add missing else clause

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
e4da2fb3ec test(ml): GPU PER integration tests — training loop + distribution correctness
5 tests: training loop cycle, priority-weighted sampling, KS distribution
test, IS weight range validation, ring buffer overwrite correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
aa6708f88d feat(ml): async GPU loss readback in gradient accumulation
Collect loss tensors on GPU during accumulation loop and perform a
single mean+to_scalar readback at the accumulation boundary instead
of N per-step CPU readbacks. Falls back to CPU sum when GPU tensors
are unavailable. Also adds missing loss_tensor_gpu field to
RegimeConditionalDQN's GradientResult construction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
8873eb4a9a feat(ml): wire GpuReplayBuffer into DQN trainer hot path
GradientResult and ComputeLossResult gain td_errors_gpu/indices_gpu
Option<Tensor> fields for GPU-resident TD errors. Trainer accumulates
GPU tensors and calls update_priorities_gpu() when available, falling
back to CPU path otherwise. DqnAgent delegates via memory().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
52ea2ac36b feat(ml): ReplayBufferType::GpuPrioritized variant with full dispatch
New variant delegates to GpuReplayBuffer for all operations. Sample
returns BatchSample with gpu_batch populated (empty CPU vecs). Added
update_priorities_gpu() for tensor-based priority updates, as_gpu_buffer()
for direct access from trainer. Existing tests unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
0939552a16 feat(ml): proportional + rank-based PER sampling and GPU priority update
Proportional: cumsum + binary search over alpha-weighted priorities.
Rank-based: sort descending, rank probabilities 1/rank^alpha, cumsum sample.
Priority update: |td_error|^alpha + epsilon scatter into buffer.
All methods return GpuBatch with IS weights normalized by max.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
c92691556b feat(ml): GpuReplayBuffer::insert_batch with ring buffer wrapping
Slice-scatter based insertion handles wrap-around by splitting into
tail + head copies. New experiences get max_priority. Tests verify
correctness at capacity boundary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
59eddb99f6 feat(ml): GpuReplayBuffer core struct with ring buffer storage
Pre-allocates all experience tensors on device at init (~47 MB for 100K
buffer). Ring buffer cursor and beta annealing state tracked CPU-side.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
0fc65717f2 feat(ml): add GpuBatch struct and gpu_batch field to BatchSample
GPU-resident batch tensors allow compute_gradients() to skip CPU→GPU
transfer when sampling from the GPU replay buffer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:57:14 +01:00
jgrusewski
c64022f819 fix(hyperopt): remove double win_rate multiplication in DQN trial summary
PerformanceMetrics::from_trades() already returns win_rate as percentage
(e.g. 57.78), but TRIAL_SUMMARY and backtest details log lines multiplied
by 100 again, producing values like 5778%. Remove the extra * 100.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:47:35 +01:00
jgrusewski
ca8b77f63a feat(dqn): wire gradient explosion and checkpoint save metrics into trainer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:34:41 +01:00
jgrusewski
2642286c86 feat(hyperopt): record elapsed_seconds metric in all hyperopt binaries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:10:02 +01:00
jgrusewski
ddacfbbff1 feat(supervised): record learning rate and epoch duration metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:10:00 +01:00
jgrusewski
afa506495f feat(ppo): record entropy, KL, advantage, explained variance, epoch duration metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:59 +01:00
jgrusewski
47f8c6109d feat(dqn): record Q-value, gradient norm, epoch duration, buffer size metrics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:58 +01:00
jgrusewski
6c7a7275de feat(fxt): add grad norm, RL diagnostics, and hyperopt trial detail to monitor
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:56 +01:00
jgrusewski
ab218121b6 feat(monitoring): wire 12 new metric→proto field mappings in group_into_sessions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:09:54 +01:00
jgrusewski
7983302a52 proto(monitoring): add 12 new TrainingSession fields for diagnostics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:00:36 +01:00
jgrusewski
b08b67a5cd feat(metrics): register 17 new Prometheus metrics with verbose gating
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:56:51 +01:00
jgrusewski
2e3ac28e1b docs: implementation plan for 17 additional training metrics
9 tasks covering: training_metrics.rs (definitions + verbose gate),
monitoring.proto (12 new fields), service.rs (match arms + tests),
fxt monitor CLI (grad norm, RL diagnostics, hyperopt trial detail),
DQN/PPO trainers (per-epoch recording), supervised binary (LR + duration),
hyperopt binaries (elapsed_seconds), and full workspace verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:48:18 +01:00
jgrusewski
477b9cf1cf docs: GPU PER sum-tree implementation plan (13 tasks)
TDD plan covering GpuReplayBuffer, proportional + rank-based GPU
sampling, priority scatter updates, async loss readback, trainer
integration, OOM fallback, and distribution correctness tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:45:27 +01:00
jgrusewski
9105921f8d docs: additional training metrics design — 17 new Prometheus metrics
Two-tier system: 12 always-on gauges (RL diagnostics, gradient health,
hyperopt intra-trial) + 5 opt-in verbose metrics gated behind
FOXHUNT_VERBOSE_METRICS=1. Extends proto, monitoring service, and CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:44:45 +01:00
jgrusewski
995c473ad9 docs: GPU-resident PER sum-tree design
Approved design for moving Prioritized Experience Replay entirely to
GPU — flat priority array with parallel prefix-sum sampling, GPU-resident
ring buffer for experiences, async loss readback. Eliminates the last
major CPU bottleneck in the DQN training loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:40:57 +01:00
jgrusewski
2f97402d5d fix(monitoring): use active_workers metric instead of kube_job query
CI training jobs run as GitLab runner pods, not K8s Jobs, so the
kube_job_status_active query always returned 0. Switch to
foxhunt_training_active_workers which is emitted by all training
binaries regardless of deployment method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:21:15 +01:00
jgrusewski
85725abd8f feat(monitoring): per-epoch Prometheus metrics + CI scrape plumbing
- DQN/PPO trainers now record epoch, loss, val_loss, batch/s every epoch
- CI training jobs get Prometheus scrape annotations via runner overrides
- Allow Prometheus (foxhunt namespace) to scrape foxhunt-ci pods
- Skip no-model metrics in monitoring service session grouping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:10:52 +01:00
jgrusewski
6b67f6193a infra: expose monitoring service via monitor.fxhnt.ai Tailscale proxy
Add DNS record, nginx gRPC proxy block, network policy for Tailscale
ingress, and auto-detect fxhnt.ai in fxt monitor URL derivation.
Show GPU telemetry even when no training sessions are active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:56:40 +01:00
jgrusewski
d9b266e1d3 infra: add monitoring-service network policy, allow web-gateway egress
monitoring-service needs egress to Prometheus (port 80/9090) and ingress
from web-gateway (port 50057). web-gateway needs egress to monitoring-service.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:44:27 +01:00
jgrusewski
6d93ee4db0 ci: add monitoring_service to deploy binary copy and restart loops
The compile-services job already builds monitoring_service but the deploy
job was missing it from the PVC copy loop and rollout restart/wait loops.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:34:50 +01:00
jgrusewski
2f193dca6d fix(ml): GPU experience collector gate — support hybrid distributional+dueling
The GPU experience collector gate checked only `dqn.dueling_q_network`
(plain dueling), but with both `use_dueling: true` AND `use_distributional: true`
(the defaults), DQN creates hybrid `dist_dueling_q_network` instead, leaving
the plain dueling fields as None. This meant the GPU collector never initialized
despite curiosity being enabled.

Fix: Add else-if fallback to check `dist_dueling_q_network`/`dist_dueling_target_network`
when plain dueling fields are None. Same fix applied to the weight sync site.

Also fix test_train_with_empty_data_completes_gracefully: reduce to 5 epochs with
early stopping disabled. The debug-mode async state machine is large enough that
empty-data epochs run ~180ms each (vs ~3ms in release), triggering both plateau
and patience-based early stopping. The test purpose is crash-freedom, not timing.

2497 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 22:32:41 +01:00