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>
7.1 KiB
RL Agent Gaps — Design Document
Date: 2026-03-03 Status: Approved Approach: Vertical slices (sequential, dependency-ordered)
Overview
Audit of DQN and PPO agents identified 7 gaps across training quality and production readiness. This design addresses all 7 in dependency order.
Implementation order:
- Position-aware state encoding (foundation)
- Regime detection (feature-based + HMM)
- Slippage modeling
- Curriculum learning (uses regime detection)
- Multi-timeframe fusion (default on)
- Online learning (needs stable architecture)
- A/B testing framework (deployment layer)
1. Position-Aware State Encoding
Problem: Agent doesn't see its own position — learns constraints by trial-and-error.
Design: Extend state vector with 4 features (51 → 55 dims):
| Feature | Normalization | Range |
|---|---|---|
current_exposure |
Direct | [-1.0, 1.0] (Short100 to Long100) |
unrealized_pnl |
Divide by EMA σ (100-bar) | Unbounded, typically [-5, 5] |
bars_in_position |
log(1 + x) / log(1001) | [0, 1] |
position_cost_basis |
Relative to current price, in bps | [-500, 500] typical |
Location: crates/ml/src/features/position_features.rs
Impact: All hidden dim configs, hyperopt bounds, walk-forward feature counts update from 51 → 55.
2. Regime Detection
Problem: detect_regime() returns hardcoded "normal". RegimeConditionalDQN has no real input.
Layer 1: Feature-Based Classifier (real-time, <1μs)
Input: rolling 100-bar window of OHLCV.
| Regime | Conditions |
|---|---|
| Trending | ADX > 25 AND Hurst > 0.55 |
| MeanReverting | Hurst < 0.45 AND vol < median |
| Volatile | vol > 2σ above mean OR ADX < 15 |
| Normal | Default |
Returns existing RegimeType enum.
Layer 2: HMM (offline, periodic refit)
- 3-state HMM (Low-Vol / Trending / Crisis) on daily returns
- Fitted during walk-forward setup, refit weekly
- Provides transition matrix for persistence estimation
- Validates feature-based classifier accuracy
Location: crates/ml/src/regime_detection/ (replace stub). feature_classifier.rs + hmm.rs.
Integration: Wire into RegimeConditionalDQN head dispatch.
3. Slippage Modeling
Problem: Fixed 15/5/10 bps costs don't reflect volume-dependent market impact.
SlippageModel trait
Two implementations:
LinearImpactModel (new default):
slippage = spread/2 + sqrt_impact * sqrt(volume / ADV)
spread: estimated from OHLCV as0.5 * (high - low) / close * normalizationsqrt_impact: per-instrument (ES ≈ 0.1, 6E ≈ 0.3 bps/√lot)ADV: 20-day rolling average daily volume- Temporary impact decays:
temp_impact * exp(-t / decay_half_life)
FixedCostModel (backward compat):
- Wraps existing 15/5/10 bps constants
Location: crates/ml/src/risk/slippage.rs
Integration: Replace fixed cost_weight in dqn/reward.rs with slippage_model.estimate_cost(action, state). Same for PPO and evaluation engine.
4. Curriculum Learning
Problem: 45-action space + all regimes from step 0 slows convergence.
3-Phase Schedule
| Phase | Exposure Actions | Regimes | Gate |
|---|---|---|---|
| 1: Basic | Flat, Long50, Short50 (27 actions) | Normal only | Sharpe > 0.5 for 3 folds |
| 2: Full Position | All 5 exposures (45 actions) | Normal + Trending | Sharpe > 0.3 for 2 folds |
| 3: All Regimes | All 45 | All | Final phase |
Mechanism:
- Action masking expanded per phase (reuse
get_valid_action_mask()) - Regime filtering via Section 2 detection
- Phase tracked in checkpoint metadata
- Manual override:
--curriculum-phase 3
Location: crates/ml/src/trainers/curriculum.rs
5. Multi-Timeframe Feature Fusion (Default On)
Problem: Single 1-minute resolution. No cross-timeframe context.
Architecture
1m bars ──→ [Encoder] ──→ emb_1m (64) ─┐
├─ resample 5m ──→ [Encoder] ──→ emb_5m (64) ─┤
├─ resample 15m ──→ [Encoder] ──→ emb_15m (64) ─┼─→ [Concat 256] ─→ [Linear 128] ─→ macro_state
└─ resample 1h ──→ [Encoder] ──→ emb_1h (64) ─┘
- Resample from 1m OHLCV on-the-fly (no extra data download)
- Per-timeframe encoder: shared-weight LSTM
- Fusion: concatenate → linear projection → 128-dim macro context
- Combined state: position features (55) + macro context (128) = 183 input dims
- Default on — no feature gate
Location: crates/ml/src/features/multi_timeframe.rs
6. Online Learning (Per-Trade)
Problem: Models freeze after walk-forward training. No adaptation to regime shifts.
Core Mechanism
- Rolling experience buffer: 10,000 transitions (configurable)
- Mini-update every 100 trades:
- DQN: sample mini-batch, TD loss, weight update
- PPO: N-step trajectory, advantages, single epoch
- EWC regularization prevents catastrophic forgetting:
- Fisher Information Matrix (diagonal approx) computed after initial training
online_loss = task_loss + λ * Σ F_i * (θ_i - θ*_i)²- λ default: 1000.0, Fisher recomputed every 10,000 steps
Safety Rails
| Rail | Config | Default |
|---|---|---|
| Gradient clipping | max_grad_norm |
1.0 |
| Learning rate | online_lr_multiplier |
0.1× base LR |
| Checkpoint rollback | Sharpe degrades > 20% over 500 trades | Auto-rollback |
| Kill switch | Rolling Sharpe < -1.0 | Freeze + alert |
Location: crates/ml/src/trainers/online_learning.rs
Integration: Called from trading_service after trade execution.
7. A/B Testing Framework
Problem: No safe mechanism to deploy and compare new models.
Architecture
Two slots: Champion (production) and Challenger (candidate).
Traffic allocation via Thompson Sampling:
- Each model: Beta(α, β) distribution on win rate
- Sample from both; higher sample gets the trade
- Initial: Champion Beta(10, 1), Challenger Beta(1, 1)
Shadow mode: Challenger runs inference but never executes (100% Champion).
Metrics per model
- Rolling Sharpe (500 trades)
- Win rate, mean PnL/trade
- Max drawdown
- Action diversity (entropy)
Promotion criteria (all required)
- Challenger Sharpe > Champion Sharpe (Bayesian P > 0.95)
- Challenger max DD ≤ 1.2 × Champion max DD
- Minimum 500 evaluated trades
- Manual approval via existing
ApproveModelgRPC
Location: crates/ml/src/deployment/ab_testing.rs
Integration: trading_service ensemble infrastructure + ApproveModel/RejectModel handlers.
Summary
| # | Feature | New Files | Impact |
|---|---|---|---|
| 1 | Position-aware state | features/position_features.rs |
State 51→55 dims |
| 2 | Regime detection | regime_detection/{feature_classifier,hmm}.rs |
Replace stub |
| 3 | Slippage model | risk/slippage.rs |
Reward computation |
| 4 | Curriculum learning | trainers/curriculum.rs |
Trainer loops |
| 5 | Multi-timeframe fusion | features/multi_timeframe.rs |
State 55→183 dims (default on) |
| 6 | Online learning | trainers/online_learning.rs |
Inference hot path |
| 7 | A/B testing | deployment/ab_testing.rs |
Trading service |