Design for 3 remaining deferred stubs from 2026-02-24 audit: - Retrain Model: fine-tune via gRPC with TrainingMode proto field - Portfolio Positions: PositionProvider trait with broker + DB fallback - ML Confidence: ensemble RPC with liquidity heuristic fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.2 KiB
Deferred Stubs Implementation — Design
Date: 2026-03-01
Branch: feature/deferred-stubs
Scope: 3 deferred stubs from 2026-02-24 audit
Context
The stub audit (2026-02-24) identified 18 findings. 8 were fixed with real implementations, 3 with honest errors, 3 acceptable, and 4 deferred. Of those 4:
- WS streams (stream_model_metrics, stream_signal_strength) — already fixed (verified 2026-03-01, both publish real data via tokio channels)
- Feature importance — correctly stubbed (no feasible path without SHAP/external lib)
That leaves 3 real deferred items to implement:
| Stub | Location | Current Behavior |
|---|---|---|
| Retrain Model | trading_service/enhanced_ml.rs:1018 |
Status::unavailable |
| Portfolio Positions | risk-data/src/var.rs:641 |
Returns empty Vec<PortfolioPosition> |
| ML Confidence | trading_agent_service/autonomous_scaling.rs:665 |
Liquidity heuristic score * 0.9 + 0.1 |
Area 1: Retrain Model — Fine-Tune via gRPC
Problem
retrain_model() returns Status::unavailable("not yet wired to ml_training_service").
The ml_training_service has StartTraining RPC but no fine-tune mode.
Design
-
Proto change: Add
TrainingModeenum (FULL,FINE_TUNE) and fields toStartTrainingRequestinservices/ml_training_service/proto/ml_training.proto:TrainingMode mode— selects full vs fine-tunestring resume_checkpoint_path— path to last checkpointuint32 max_epochs— fine-tune uses fewer epochs (default 10)
-
trading_service client: Add
MlTrainingServiceClienttoEnhancedMLServiceImplviaOnceCelllazy initialization. Endpoint configured via env varML_TRAINING_SERVICE_URL(defaulthttp://ml-training-service:50052). -
retrain_model() handler:
- Look up
RuntimeModelInfofor the requested model_name - Map model_name →
CommonModelTypeenum - Build
StartTrainingRequestwith mode=FINE_TUNE, checkpoint path, max_epochs=10 - Call
client.start_training(request).await - Return
RetrainModelResponse { job_id, status: "started" }
- Look up
-
ml_training_service handler: In
start_training():- If mode=FINE_TUNE: load checkpoint, train on recent 1-month data window, 10 epochs
- If mode=FULL: existing behavior (walk-forward, full training)
Risk
Low-Medium. Proto change is additive (backward compatible). The fine-tune code path in ml_training_service builds on existing training infrastructure.
Area 2: Portfolio Positions — Broker + DB Fallback
Problem
get_portfolio_positions() in risk-data/src/var.rs returns an empty Vec,
causing VaR calculations to fail with "No positions found for portfolio".
Design
-
PositionProvider trait in
crates/risk-data/src/position_provider.rs:#[async_trait] pub trait PositionProvider: Send + Sync { async fn get_positions(&self, portfolio_id: &str) -> RiskDataResult<Vec<PortfolioPosition>>; } -
DatabasePositionProvider: Queries
portfolio_positionstable via SQLX. Schema:(id, portfolio_id, symbol, quantity, avg_entry_price, current_price, side, updated_at). Uses existing SQLX offline mode. -
BrokerPositionProvider: Calls broker_gateway_service gRPC
GetPositionsRPC. Falls back gracefully if broker is unreachable. -
Fallback chain in
get_portfolio_positions():- Try broker (live positions, most accurate)
- Try DB (cached positions from fill events)
- Return error if both fail
-
Position sync: Trading engine's fill handler writes/updates positions in DB on each fill event. This keeps the DB cache fresh even when broker is offline.
-
SQL migration: Add
portfolio_positionstable via SQLX migration.
Risk
Medium. Requires DB schema change and new SQLX queries. The broker_gateway is Phase 2 (FIX routing MVP), so broker path may not work immediately — DB path is the primary functional path.
Area 3: ML Confidence — Ensemble RPC
Problem
compute_tier_allocations() in autonomous_scaling.rs uses a liquidity-based
heuristic for ML confidence instead of calling the ensemble service.
Design
-
Add optional gRPC client:
Option<MlServiceClient>on the autonomous scaling service struct. Initialized from env varML_SERVICE_URL. -
Call GetEnsembleVote: Per instrument in
compute_tier_allocations():- Build
EnsembleRequest { model_name: "all", symbol: Some(instrument.symbol) } - Call
client.get_ensemble_vote(request).await - Extract confidence from response
- Build
-
Fallback: On RPC failure, use existing heuristic
liquidity_score * 0.9 + 0.1 -
Caching: Cache ensemble responses for 5 seconds per symbol to avoid excessive RPC calls during allocation computation.
Risk
Low. The fallback is already tested and functional. This is purely additive.
Execution Order
- Retrain Model (smallest scope, unblocks model lifecycle)
- ML Confidence (low complexity, improves trading accuracy)
- Portfolio Positions (largest scope, requires DB migration)
Verification
After each area:
SQLX_OFFLINE=true cargo check --workspaceSQLX_OFFLINE=true cargo clippy -p <affected_crates> --lib -- -D warningsSQLX_OFFLINE=true cargo test -p <affected_crates> --lib