- trading_engine: replace 20 drop(Copy) with let _ = (drop on Copy is no-op)
- data: remove 4 unnecessary crate::error:: qualifications
- ml: remove stale #[allow] attribute on inference.rs
- web-gateway: allow dead_code on stub route body fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete 22 orphaned files (.backup, .broken_backup, .old, .rej, .disabled)
- Remove duplicate KillSwitch stub from risk_engine.rs, use AtomicKillSwitch
- Deduplicate UnixSocketKillSwitch via re-export from unix_socket module
- Rename StreamingConfig → EventStreamingConfig to resolve naming collision
- Guard MockTradingRepository behind #[cfg(test)] in trading_service
- Replace adaptive-strategy EnsembleConfig with re-export from ml crate
- Merge error_recovery.rs fields into canonical RetryConfig (circuit breaker,
jitter, HFT precision mode) and delete the 328-line dead module
- Replace local 3-variant RiskError with risk::error::RiskError import
- Fix all RetryConfig struct literals with ..Default::default()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ensemble/model.rs: replace silent .read().ok() with map_err+tracing::error for both models and signals RwLock in health_check
- batch_tuning_manager.rs: replace let _ = stop_tuning_job() with if let Err + tracing::warn
- orchestrator.rs: replace let _ = broadcaster.send() with if let Err + tracing::warn
- tuning_manager.rs: replace let _ = progress_tx.send() with if let Err + tracing::warn
- trial_executor.rs: replace let _ = result_tx.send() with if let Err + tracing::warn
- Use {:?} for SendError types whose inner value does not implement Display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace barrier_label.unwrap() with unwrap_or(0) at two debug log sites (lines 1464, 1700)
- Replace self.nstep_buffer.take().unwrap() with let-else pattern to safely skip on None
- Replace self.safety_loss_history.back().unwrap() with let-else and intermediate prev_loss_raw
- Add #[allow(clippy::unwrap_used, clippy::expect_used)] on lazy_static! block in inference.rs
with SAFETY comment explaining Prometheus string-literal registration is infallible
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 10 .as_ref().unwrap() calls on Option<QuantizedTensor> weight fields
(q_weights, k_weights, v_weights, o_weights) and attention_cache with
.as_ref().ok_or_else(|| MLError::ModelError(...))? to satisfy the
clippy::unwrap_used deny lint. Affected methods: build_cache,
forward_with_mask, compute_projections_slow, and the test
test_attention_weights_sum_to_one.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- allocation.rs: get_asset_volatilities now uses flat 0.20 (20% annual vol)
instead of index-scaled 0.15+i*0.05; get_covariance_matrix diagonal is
0.04 (0.20^2) instead of 0.0225; get_ml_predictions uses 0.0 (neutral)
instead of 0.05+i*0.02. All three emit tracing::warn so mock state is
visible in production logs.
- training.rs: train_all emits tracing::warn that it is using a placeholder
loop and directs callers to use model-specific trainers for production.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- emergency_stop: delegates to TradingServiceKillSwitch.emergency_shutdown()
which activates the global AtomicKillSwitch; returns Status::unavailable
when kill_switch_system is None rather than silently succeeding
- validate_order: reads max_order_quantity from config repository (falls back
to 1_000_000); additionally calls RiskEngine.check_var_limit() for VaR
validation when symbol and price are provided
- get_va_r: uses RiskEngine.calculate_marginal_var() for real VaR with a
parametric fallback; per-symbol marginal VaRs computed individually
- get_risk_metrics: derives portfolio_var_1d from RiskEngine; scales to 5d
and 30d via sqrt-of-time rule; remaining fields (Sharpe, beta, alpha,
current_drawdown) keep placeholder values with explicit TODO comments
- fix(risk): correct stop_monitoring() self.error_rate() → self.get_health_metrics()
(pre-existing typo that blocked compilation of trading_service)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all 8 `.lock().unwrap()` calls in the Rayon parallel closure of
`quantize_varmap_parallel` with `.lock().unwrap_or_else(|e| e.into_inner())`
so that mutex poisoning (caused by a panicking Rayon thread) does not
cascade and crash all other worker threads — the guard is recovered from
the poisoned state and processing continues safely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace `.expect("INVARIANT: rate_limit_per_second must be > 0")` with
`.unwrap_or(NonZeroU32::new(100).expect("100 > 0"))` — falls back to 100 rps
when config value is zero instead of panicking
- Replace `.expect("INVARIANT: Semaphore should never be closed")` in batch
processor loop with a match that logs and breaks cleanly on semaphore closure
- Replace `.expect("Failed to clone Mamba2SSM")` in Clone impl with a match
that logs the error and calls `std::process::abort()` — makes the panic
explicit and avoids unwrap_used lint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace unwrap() on NonZeroUsize::new(config.max_models) with a safe
fallback to capacity 16, and replace unwrap() on Mutex::lock() in the
Debug impl with ok().map(...).unwrap_or(0) to avoid panics under
poisoned-mutex or zero-capacity conditions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the no-op apply_layer_norm() that returned input unchanged with
real layer normalization using layer_norm_with_fallback. Initialize
LayerNormParams with proper weight (ones) and bias (zeros) tensors in
from_grn constructor instead of None placeholders.
Replace the 2-feature placeholder (close, volume) in create_training_samples()
with the full 51-dimension extract_ml_features() pipeline. The UnifiedDataLoader
now converts MarketDataContainers to OHLCVBars and runs the production feature
extractor when use_unified_extractor is enabled, falling back to the basic
2-feature path if extraction fails or the extractor is disabled. Also populates
feature metadata with the 51 named features matching extraction.rs ordering.
Production trading must never rely on simulated predictions. The ensemble
coordinator now requires real model adapters and returns errors when none
are registered or all fail inference, instead of silently falling back to
mock/simulated predictions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace empty placeholder safetensors file with actual model weight
serialization using the VarMap, matching the DQN trainable adapter pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 6 unwrap() calls with safe error handling in DQN IQN code:
- Production: 3 unwrap() on iqn_network/iqn_target_network replaced with
ok_or_else returning MLError::ModelError for clear diagnostics
- Tests: 2 result.unwrap() replaced with ?, 2 DQN::new().unwrap() replaced
with ? after changing test signatures to return anyhow::Result<()>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add #[allow(unsafe_code)] on each unsafe impl Send/Sync for the four
inference adapters (DQN, PPO, TFT, Mamba2). The SAFETY comments explain
why these are sound — Mutex provides exclusive access.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PaperBroker: simulated fills with configurable slippage and commission.
PnLTracker: rolling Sharpe ratio (252-day window), max drawdown, cumulative return.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CampaignResults with serde serialization. run_campaign() orchestrates
ArgminOptimizer for DQN hyperopt, saves best_params.json and
campaign_summary.json to timestamped results directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lightweight synchronous coordinator that wraps N ModelInferenceAdapter
instances. Aggregates predictions via confidence-weighted voting with
graceful degradation for unready or failing models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DQN adapter: save/load/predict round-trip validation
- Coordinator: full ensemble with real DQN adapter integration test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace mock predictions with real model inference when adapters are
registered. Falls back to mock predictions for models without adapters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements TftInferenceAdapter that wraps the Temporal Fusion Transformer
for ensemble prediction. Unlike single-step models (DQN, PPO), TFT requires
a sequence of observations before running inference. The adapter maintains
an internal VecDeque buffer that collects sequence_length feature vectors,
then constructs static/historical/future tensors for the TFT forward pass.
Median quantile is mapped to directional signal via smooth saturating
function; IQR provides confidence estimation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement ModelInferenceAdapter for DQN (Q-value argmax -> direction + softmax confidence)
and PPO (probability-weighted action values -> direction + max prob confidence) with
zero-padding for mismatched feature dimensions and 6 passing unit tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ObjectiveMode enum (EpisodeReward/Sharpe) to DQNTrainer and a
TwoPhaseObjective trait + optimize_two_phase() method to ArgminOptimizer.
Phase A optimizes episode reward for fast convergence, Phase B (pending
model Clone support) refines with Sharpe ratio. This keeps the static
extract_objective trait method untouched by separating objective switching
into instance-level state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add load_ofi_features() helper to both DQN and PPO trainer adapters
that loads MBP10 snapshots from a sibling mbp10/ directory, computes
8-slot OFI feature vectors via OFICalculator, and overlays them onto
positions 43-50 of the training feature arrays. Gracefully falls back
to zero-padded features when MBP10 data is not available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add use_qr_dqn, num_quantiles, qr_kappa fields to DQNHyperparameters
and pass them from DQNParams (hyperopt) through to DQNConfig (agent),
replacing hardcoded IQN values in the trainer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add num_quantiles and qr_kappa to the continuous search space for
Quantile Regression DQN, replacing the disabled C51 distributional RL
(BUG #36). QR-DQN is enabled by default with 32 quantiles and kappa=1.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace flat read_dir() with recursive collect_dbn_files_recursive() in
both DQN and PPO hyperopt adapters so .dbn files inside symbol
subdirectories (6E.FUT/, ES.FUT/, NQ.FUT/, ZN.FUT/) are discovered
automatically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the TODO stub with a working Hyperband implementation that
applies Successive Halving pruning only at rung epochs (max_resource/eta^k).
Between rungs, trials always continue. Adds two tests verifying pruning
at rung epochs and non-pruning between rungs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Single-sample latency measurement was flaky under concurrent test load.
Using median eliminates outlier sensitivity (127μs median vs 1154μs spike).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Implement load_from_dbn() for both PPO and DQN hyperopt adapters
using dbn::DbnDecoder (same pattern as RealDataLoader)
- Fix PPO feature extraction: [f64;51] → [f32;54] with zero-padded
portfolio state (was silently dropping all samples due to len==54 check)
- Add NaN/Inf → 1e6 penalty in optimizer for non-finite objectives
- Fix partial_cmp().unwrap() panic when comparing NaN objectives
- Add ensemble real-model validation test (DQN + PPO trained on
real 6E.FUT data, predictions aggregated through ensemble)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create PpoStrategy and PpoLstmStrategy implementing ValidatableStrategy
to run PPO through walk-forward validation with DSR, PBO, and permutation
tests. Both variants validated on real 6E.FUT data (29,937 bars, 15 folds).
Key implementation detail: LSTM hidden states are detached from the
computation graph after each step to prevent stack overflow from
unbounded graph growth across 30k+ sequential forward passes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add accumulation_steps config to PPOConfig with gradient accumulation
in update_mlp() using existing accumulate_grads/scale_grads utilities
- Add clip_epsilon_high: Option<f32> for asymmetric PPO clipping to
prevent entropy collapse during long training
- Rename WorkingPPO → PPO for consistency with DQN naming convention
- Add pub type WorkingPPO = PPO for backward compatibility
- Fix PPOConfig struct literals in trading_service and hyperopt adapter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply max_grad_norm clipping in update_mlp() and update_lstm() by splitting
backward_step into backward + norm computation + conditional scaling.
Add from_varbuilder() to LSTMPolicyNetwork and LSTMValueNetwork for
checkpoint deserialization. Remove TODO early-return error in load_checkpoint()
and add LSTM branching for actor/critic loading.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unnecessary unsqueeze(1) in FlowPolicy log_prob and entropy
(shapes should be [batch_size], not [batch_size, 1])
- Update flow_policy tests to expect correct [batch_size] shape
- Simplify kelly position sizing test with helper function
- Clean up example files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Bug #42 sign inversion diagnostic was logging thousands of lines to
stdout on every training run. Now gated behind RUST_LOG=debug level.
Removed unused expected_position variable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The default DQN agent (RegimeConditionalDQN with 3 regime heads) was
silently failing all gradient accumulation calls because only the
Standard DQN variant was supported. All training steps errored out and
loss was recorded as 0.0.
Changes:
- Add compute_gradients() to RegimeConditionalDQN: classifies batch by
regime, routes sub-batches to heads, merges gradient stores (no key
collisions since heads have independent parameters)
- Add apply_accumulated_gradients(): applies to heads with initialized
optimizers, skips uninitialized heads (no training data for that regime)
- Add optimizer_vars(): returns combined vars from all heads
- Update DQNAgentType dispatch to delegate to RegimeConditional
- Re-export GradientResult from dqn module
- Fix batch size validation tests for AutoBatchSizer clamping behavior
Verified: convergence test shows 4.8% relative difference between
accumulated (16x4) and direct (64x1) training paths (threshold: 30%).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DQNTrainer::new() now clamps oversized batch sizes to the safe GPU limit
instead of rejecting them. Updated test_batch_size_validation and
test_gpu_batch_limit_230_enforced to assert is_ok() with clamping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>