Coordinator (ml/src/integration/coordinator.rs):
- Delete 9 fake heuristic methods (500+ lines) that pretended to be
real DQN/TFT/TGGN/LNN/Mamba predictions using sin()/tanh() math
- Make generate_model_specific_prediction() return Err instead of
fake predictions — prevents trading on fabricated signals
- Make ensemble fault-tolerant: skip failed models instead of
failing entire ensemble (execute_parallel/execute_sequential)
- Remove double-fallback in execute_single_model error path
Enhanced ML (trading_service):
- get_model_performance(): compute accuracy from real
inference_count/error_count instead of returning all zeros
- get_feature_importance(): return Status::unavailable instead of
hardcoded fake values — honest about missing SHAP implementation
Autonomous scaling (trading_agent_service):
- diversification_score: compute real HHI from instrument volume
distribution instead of hardcoded 0.8
- ml_confidence: keep liquidity heuristic but remove warn!() spam
Position limiter (risk):
- Remove redundant portfolio_id field from CachedPosition — the
DashMap key already provides account-based isolation
- Remove misleading "stub" comment — was not a stub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add debug_assert_eq! guards in 4 train_baseline functions to catch
bar/feature length misalignment at debug time (#4)
- Remove "last sample targets itself" block in hyperopt PPO adapter
that created ~0 return sample biasing toward HOLD (#5)
- Align hyperopt state_dim 54→51 and num_actions 45→3 to match
train_baseline architecture, making tuned hyperparams transferable (#6)
- Use greedy_action() in evaluate_baseline PPO eval for deterministic results
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared DBN loading, timestamp dedup, and spread cost helpers used by
train_baseline, evaluate_baseline, and hyperopt_baseline extracted to
ml/examples/baseline_common/mod.rs to eliminate ~190 lines of duplication.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three critical PPO fixes:
1. Add PPO::act_with_log_prob() returning (action, log_prob, value).
The existing act() discarded the policy log-probability, making
PPO importance sampling use wrong ratios during training.
2. Cap trajectory length with --max-steps-per-epoch in train_baseline
PPO path. DQN already had this limit; PPO iterated all features
(~500K per fold), causing OOM on 4GB GPU.
3. Replace random actions and fake log_prob/value in hyperopt PPO
adapter with real agent.act_with_log_prob() calls. Trajectories
now reflect actual policy behavior for meaningful hyperopt.
Also adds warmup offset alignment to PPO trajectory collection
(matching the DQN fix) and fixes .unwrap() in test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use chrono-tz America/New_York for correct EST/EDT trading session
boundaries (was hardcoded UTC-5, off by 1h during daylight saving)
- Normalize effective weights to sum to 1.0 before signal aggregation
(raw weights could sum to anything, biasing the ensemble)
- Skip adapter predictions that return NaN/Inf direction or confidence
instead of letting them poison the weighted average
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix zstd decoder in train_baseline.rs and evaluate_baseline.rs (same
pattern as hyperopt adapters — branch on .dbn.zst extension). Add CLI
flags for walk-forward config (train/val/test/step months), learning
rate, and max-steps-per-epoch to make pipeline validation feasible.
Pipeline validated end-to-end: hyperopt (5 trials, best Sharpe 2.37) →
walk-forward training (4 folds, 6/1/1 month windows on ES.FUT) →
evaluation (4 fold test sets, checkpoints + norm stats saved).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PPO hyperopt: Split DATA 80/20 for train/val (not trajectory count).
Previously indexed training_data[..num_trajectories] which was only
16 samples from 56K — causing num_batches=0 and NaN from 0/0 division.
Now properly splits data array and uses min(64, num_train) for batch
episodes with ceil division for batch count.
DQN: Fix hardcoded "45 actions" in diagnostic log (now uses actual
q_vec.len()). Replace indexed[..top_n] slice with .take(top_n)
iterator to prevent potential slice panic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Databento data pipeline stored files as .dbn.zst in symbol subdirectories
(e.g., ES.FUT/ES.FUT_2024-Q1.dbn.zst), but the hyperopt adapters and DQN
data loader only matched .dbn extension and used flat directory listing.
Three fixes:
- Match both .dbn and .dbn.zst file extensions in collect_dbn_files_recursive
- Use recursive directory traversal instead of flat read_dir
- Branch on extension to use Decoder::from_zstd_file() for .dbn.zst files
(from_file() doesn't handle zstd decompression)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The download binary was using the default SType::RawSymbol which
returned empty DBN files (95 bytes) for parent symbols like ES.FUT.
Adding SType::Parent resolves the symbol mapping. Also adjusted end
date to 2026-02-22 (latest available data).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add #![allow(unused_crate_dependencies)] for consistency with other ml tests
- Extract EXPECTED_FEATURE_DIM constant (replaces magic number 51)
- Add validated_folds counter to prevent vacuous pass in walk-forward test
- Replace unwrap_or_else(panic) with match pattern
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three integration tests validating the full pipeline end-to-end:
1. Feature extraction: 90-day synthetic bars -> 51-dim features, no NaN/Inf
2. Walk-forward with normalization: 24-month bars -> windows -> NormStats from train only -> normalized mean ~0
3. No lookahead bias: train timestamps < val start, val timestamps < test start
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Loads trained DQN/PPO checkpoints, runs greedy inference on walk-forward
test windows, computes Sharpe ratio, max drawdown, win rate, profit factor,
and total return per fold. Outputs a JSON evaluation report with aggregate
metrics and sanity checks (beats-random, action diversity, fold consistency).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ml/examples/train_baseline.rs that trains DQN and PPO models using
expanding walk-forward windows on real Databento OHLCV data.
Features:
- CLI args via clap (--model, --epochs, --batch-size, --data-dir, etc.)
- Recursive .dbn.zst file discovery and OHLCV bar loading
- 51-dim feature extraction via extract_ml_features()
- Walk-forward window generation with NormStats per fold
- DQN training loop with epsilon-greedy, experience replay, early stopping
- PPO training loop with GAE, trajectory collection, early stopping
- PnL-based reward (BUY/SELL/HOLD)
- Safetensors checkpoint saving per fold
- NormStats JSON export for evaluation reproducibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements expanding-window walk-forward cross-validation for time-series
ML models, preventing lookahead bias by always evaluating on unseen future
data. Includes z-score NormStats computed from training splits only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds `ml/examples/download_baseline.rs` that downloads 730 days of
Databento OHLCV-1m data in quarterly chunks for 4 CME futures symbols.
Features:
- Reads universe config from TOML (symbols, date range, dataset)
- Splits date range into calendar-quarter chunks (~90 days each)
- Resume support: skips existing non-empty files
- Uses `get_range_to_file` for streaming writes to .dbn.zst
- Dry-run mode with cost estimate ($0.12/symbol/day)
- Confirmation prompt (skippable with --yes)
- Per-file progress with timing and byte counts
- Failure-tolerant: logs errors and continues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sequence-buffered adapter with 3-layer MLP projection
(flat_dim -> hidden -> hidden/2 -> 1). Ring buffer collects
seq_len feature vectors, returns neutral prediction until full,
then flattens and projects through the MLP with sigmoid output
mapping to direction [-1,1] and confidence [0,1].
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove panic!() calls from test_futures_baseline_micro_mapping,
use map()+Some() pattern consistent with rest of test suite
- Make DatasetSpec::from_universe() accept a name parameter instead
of hardcoding "futures-baseline"
- Add doc comment to UniverseConfigMeta explaining dead_code fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove duplicate AssetClass enum from data_pipeline module and replace
with a re-export from asset_selection, which is now the canonical
location. This ensures data_pipeline::AssetClass and
asset_selection::AssetClass are the same type, enabling direct
comparison and preventing subtle type mismatch bugs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gate optimizer adjusts conviction gate thresholds based on win-rate per
confidence bucket with cooldown and kill switch safety rails. Model
registry provides lifecycle management (Candidate → Staging → Production
→ Archived) with InMemoryModelRegistry for testing. P&L attribution
decomposes realized trade P&L into per-model contributions using signal
alignment.
24 new tests across 3 modules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces the asset_selection module with AssetClass enum (Equity/ETF/Future),
UniverseAsset struct, and AssetUniverse with filtering, lookup, and a us_starter()
preset of 7 highly liquid US assets. Includes scorer/selector placeholder stubs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adjusts model weights based on rolling Sharpe ratios with:
- EMA smoothing (alpha=0.1)
- Bounds: [0.05, 0.40] per model
- Max step: 0.03 per cycle
- 24h cooldown, 7-day grace period for new models
- Kill switch freeze/unfreeze
8 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds optional ConvictionGateEvaluator field to EnsembleCoordinator.
When configured, predict() evaluates all 7 gates before returning.
If any gate rejects, returns HOLD with rejection metadata.
Includes quorum ratio, trading session, and regime volatility helpers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>